Java Generics - Краткое руководство
Было бы неплохо, если бы мы могли написать единственный метод сортировки, который мог бы сортировать элементы в массиве Integer, массиве String или массиве любого типа, который поддерживает упорядочение.
Универсальные методы и универсальные классы Java позволяют программистам определять с помощью одного объявления метода набор связанных методов или с помощью одного объявления класса набор связанных типов, соответственно.
Обобщения также обеспечивают безопасность типов во время компиляции, что позволяет программистам обнаруживать недопустимые типы во время компиляции.
Используя универсальную концепцию Java, мы могли бы написать универсальный метод для сортировки массива объектов, а затем вызвать универсальный метод с целочисленными массивами, двойными массивами, строковыми массивами и т. Д. Для сортировки элементов массива.
Настройка локальной среды
JUnit - это платформа для Java, поэтому самое первое требование - установить JDK на вашем компьютере.
Системные требования
JDK | 1.5 или выше. |
---|---|
объем памяти | Нет минимальных требований. |
Дисковое пространство | Нет минимальных требований. |
Операционная система | Нет минимальных требований. |
Шаг 1. Проверьте установку Java на вашем компьютере
Прежде всего, откройте консоль и выполните java-команду в зависимости от операционной системы, над которой вы работаете.
Операционные системы | Задача | Команда |
---|---|---|
Windows | Открыть командную консоль | c: \> java -версия |
Linux | Открыть командный терминал | $ java -версия |
Mac | Открыть Терминал | машина: <joseph $ java -version |
Давайте проверим вывод для всех операционных систем -
Операционные системы | Вывод |
---|---|
Windows | версия java "1.6.0_21" Среда выполнения Java (TM) SE (сборка 1.6.0_21-b07) Клиентская виртуальная машина Java HotSpot (TM) (сборка 17.0-b17, смешанный режим, совместное использование) |
Linux | версия java "1.6.0_21" Среда выполнения Java (TM) SE (сборка 1.6.0_21-b07) Клиентская виртуальная машина Java HotSpot (TM) (сборка 17.0-b17, смешанный режим, совместное использование) |
Mac | версия java "1.6.0_21" Среда выполнения Java (TM) SE (сборка 1.6.0_21-b07) 64-разрядная серверная виртуальная машина Java HotSpot (TM) (сборка 17.0-b17, смешанный режим, совместное использование) |
Если в вашей системе не установлена Java, загрузите Java Software Development Kit (SDK) по следующей ссылке https://www.oracle.com. Мы предполагаем, что Java 1.6.0_21 является установленной версией для этого руководства.
Шаг 2: Установите среду JAVA
Установить JAVA_HOMEпеременная среды, указывающая на расположение базового каталога, в котором установлена Java на вашем компьютере. Например.
Операционные системы | Вывод |
---|---|
Windows | Установите для переменной среды JAVA_HOME значение C: \ Program Files \ Java \ jdk1.6.0_21 |
Linux | экспорт JAVA_HOME = / usr / local / java-current |
Mac | экспорт JAVA_HOME = / Библиотека / Java / Home |
Добавьте расположение компилятора Java в системный путь.
Операционные системы | Вывод |
---|---|
Windows | Добавить строку C:\Program Files\Java\jdk1.6.0_21\bin в конце системной переменной, Path. |
Linux | экспорт PATH = $PATH:$JAVA_HOME / bin / |
Mac | не требуется |
Проверьте установку Java с помощью команды java -version как объяснено выше.
Объявление универсального класса выглядит как объявление неуниверсального класса, за исключением того, что за именем класса следует раздел параметров типа.
Раздел параметров типа универсального класса может иметь один или несколько параметров типа, разделенных запятыми. Эти классы известны как параметризованные классы или параметризованные типы, потому что они принимают один или несколько параметров.
Синтаксис
public class Box<T> {
private T t;
}
где
Box - Box - это универсальный класс.
T- Параметр универсального типа, передаваемый универсальному классу. Может принимать любой объект.
t - Экземпляр родового типа T.
Описание
T - это параметр типа, передаваемый в универсальный класс Box, и его следует передавать при создании объекта Box.
пример
Создайте следующую программу Java, используя любой редактор по вашему выбору.
GenericsTester.java
package com.tutorialspoint;
public class GenericsTester {
public static void main(String[] args) {
Box<Integer> integerBox = new Box<Integer>();
Box<String> stringBox = new Box<String>();
integerBox.add(new Integer(10));
stringBox.add(new String("Hello World"));
System.out.printf("Integer Value :%d\n", integerBox.get());
System.out.printf("String Value :%s\n", stringBox.get());
}
}
class Box<T> {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
}
Это даст следующий результат.
Вывод
Integer Value :10
String Value :Hello World
По соглашению имена параметров типа называются одиночными заглавными буквами, чтобы параметр типа можно было легко отличить от обычного имени класса или интерфейса. Ниже приведен список часто используемых имен параметров типа -
E - Элемент и в основном используется платформой Java Collections.
K - Ключ, и в основном используется для представления типа параметра ключа карты.
V - Значение, и в основном используется для представления типа параметра значения карты.
N - Число, и в основном используется для представления чисел.
T - Тип и в основном используется для представления первого параметра универсального типа.
S - Тип и в основном используется для представления второго параметра универсального типа.
U - Тип и в основном используется для представления третьего параметра универсального типа.
V - Тип и в основном используется для представления четвертого параметра универсального типа.
В следующем примере будет продемонстрирована вышеупомянутая концепция.
пример
Создайте следующую программу Java, используя любой редактор по вашему выбору.
GenericsTester.java
package com.tutorialspoint;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class GenericsTester {
public static void main(String[] args) {
Box<Integer, String> box = new Box<Integer, String>();
box.add(Integer.valueOf(10),"Hello World");
System.out.printf("Integer Value :%d\n", box.getFirst());
System.out.printf("String Value :%s\n", box.getSecond());
Pair<String, Integer> pair = new Pair<String, Integer>();
pair.addKeyValue("1", Integer.valueOf(10));
System.out.printf("(Pair)Integer Value :%d\n", pair.getValue("1"));
CustomList<Box> list = new CustomList<Box>();
list.addItem(box);
System.out.printf("(CustomList)Integer Value :%d\n", list.getItem(0).getFirst());
}
}
class Box<T, S> {
private T t;
private S s;
public void add(T t, S s) {
this.t = t;
this.s = s;
}
public T getFirst() {
return t;
}
public S getSecond() {
return s;
}
}
class Pair<K,V>{
private Map<K,V> map = new HashMap<K,V>();
public void addKeyValue(K key, V value) {
map.put(key, value);
}
public V getValue(K key) {
return map.get(key);
}
}
class CustomList<E>{
private List<E> list = new ArrayList<E>();
public void addItem(E value) {
list.add(value);
}
public E getItem(int index) {
return list.get(index);
}
}
Это даст следующий результат.
Вывод
Integer Value :10
String Value :Hello World
(Pair)Integer Value :10
(CustomList)Integer Value :10
Вывод типа представляет собой способность компилятора Java просматривать вызов метода и его соответствующее объявление для проверки и определения аргумента (ов) типа. Алгоритм вывода проверяет типы аргументов, и, если они доступны, возвращается назначенный тип. Алгоритмы вывода пытаются найти определенный тип, который может заполнить все параметры типа.
Компилятор генерирует предупреждение о непроверенном преобразовании, если определение типа не используется.
Синтаксис
Box<Integer> integerBox = new Box<>();
где
Box - Box - это универсальный класс.
<> - Оператор "ромб" обозначает вывод типа.
Описание
С помощью оператора «ромб» компилятор определяет тип параметра. Этот оператор доступен начиная с версии Java SE 7.
пример
Создайте следующую программу Java, используя любой редактор по вашему выбору.
GenericsTester.java
package com.tutorialspoint;
public class GenericsTester {
public static void main(String[] args) {
//type inference
Box<Integer> integerBox = new Box<>();
//unchecked conversion warning
Box<String> stringBox = new Box<String>();
integerBox.add(new Integer(10));
stringBox.add(new String("Hello World"));
System.out.printf("Integer Value :%d\n", integerBox.get());
System.out.printf("String Value :%s\n", stringBox.get());
}
}
class Box<T> {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
}
Это даст следующий результат.
Вывод
Integer Value :10
String Value :Hello World
Вы можете написать одно объявление универсального метода, которое может вызываться с аргументами разных типов. В зависимости от типов аргументов, передаваемых универсальному методу, компилятор обрабатывает каждый вызов метода соответствующим образом. Ниже приведены правила для определения общих методов.
Все объявления универсальных методов имеют раздел параметров типа, разделенный угловыми скобками (<и>), который предшествует типу возвращаемого метода (<E> в следующем примере).
Каждый раздел параметров типа содержит один или несколько параметров типа, разделенных запятыми. Параметр типа, также известный как переменная типа, представляет собой идентификатор, определяющий имя универсального типа.
Параметры типа могут использоваться для объявления возвращаемого типа и действовать в качестве заполнителей для типов аргументов, переданных универсальному методу, которые известны как аргументы фактического типа.
Тело универсального метода объявляется так же, как и тело любого другого метода. Обратите внимание, что параметры типа могут представлять только ссылочные типы, но не примитивные типы (например, int, double и char).
пример
В следующем примере показано, как мы можем распечатать массив другого типа с помощью одного универсального метода.
public class GenericMethodTest {
// generic method printArray
public static < E > void printArray( E[] inputArray ) {
// Display array elements
for(E element : inputArray) {
System.out.printf("%s ", element);
}
System.out.println();
}
public static void main(String args[]) {
// Create arrays of Integer, Double and Character
Integer[] intArray = { 1, 2, 3, 4, 5 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
System.out.println("Array integerArray contains:");
printArray(intArray); // pass an Integer array
System.out.println("\nArray doubleArray contains:");
printArray(doubleArray); // pass a Double array
System.out.println("\nArray characterArray contains:");
printArray(charArray); // pass a Character array
}
}
Это даст следующий результат -
Вывод
Array integerArray contains:
1 2 3 4 5
Array doubleArray contains:
1.1 2.2 3.3 4.4
Array characterArray contains:
H E L L O
Универсальный класс может иметь несколько параметров типа. В следующем примере будет продемонстрирована вышеупомянутая концепция.
пример
Создайте следующую программу Java, используя любой редактор по вашему выбору.
GenericsTester.java
package com.tutorialspoint;
public class GenericsTester {
public static void main(String[] args) {
Box<Integer, String> box = new Box<Integer, String>();
box.add(Integer.valueOf(10),"Hello World");
System.out.printf("Integer Value :%d\n", box.getFirst());
System.out.printf("String Value :%s\n", box.getSecond());
Box<String, String> box1 = new Box<String, String>();
box1.add("Message","Hello World");
System.out.printf("String Value :%s\n", box1.getFirst());
System.out.printf("String Value :%s\n", box1.getSecond());
}
}
class Box<T, S> {
private T t;
private S s;
public void add(T t, S s) {
this.t = t;
this.s = s;
}
public T getFirst() {
return t;
}
public S getSecond() {
return s;
}
}
Это даст следующий результат.
Вывод
Integer Value :10
String Value :Hello World
String Value :Message
String Value :Hello World
Универсальный класс может иметь параметризованные типы, где параметр типа может быть заменен параметризованным типом. В следующем примере будет продемонстрирована вышеупомянутая концепция.
пример
Создайте следующую программу Java, используя любой редактор по вашему выбору.
GenericsTester.java
package com.tutorialspoint;
import java.util.ArrayList;
import java.util.List;
public class GenericsTester {
public static void main(String[] args) {
Box<Integer, List<String>> box
= new Box<Integer, List<String>>();
List<String> messages = new ArrayList<String>();
messages.add("Hi");
messages.add("Hello");
messages.add("Bye");
box.add(Integer.valueOf(10),messages);
System.out.printf("Integer Value :%d\n", box.getFirst());
System.out.printf("String Value :%s\n", box.getSecond());
}
}
class Box<T, S> {
private T t;
private S s;
public void add(T t, S s) {
this.t = t;
this.s = s;
}
public T getFirst() {
return t;
}
public S getSecond() {
return s;
}
}
Это даст следующий результат.
Вывод
Integer Value :10
String Value :[Hi, Hello, Bye]
Необработанный тип - это объект универсального класса или интерфейса, если аргументы его типа не передаются во время его создания. В следующем примере будет продемонстрирована вышеупомянутая концепция.
пример
Создайте следующую программу Java, используя любой редактор по вашему выбору.
GenericsTester.java
package com.tutorialspoint;
public class GenericsTester {
public static void main(String[] args) {
Box<Integer> box = new Box<Integer>();
box.set(Integer.valueOf(10));
System.out.printf("Integer Value :%d\n", box.getData());
Box rawBox = new Box();
//No warning
rawBox = box;
System.out.printf("Integer Value :%d\n", rawBox.getData());
//Warning for unchecked invocation to set(T)
rawBox.set(Integer.valueOf(10));
System.out.printf("Integer Value :%d\n", rawBox.getData());
//Warning for unchecked conversion
box = rawBox;
System.out.printf("Integer Value :%d\n", box.getData());
}
}
class Box<T> {
private T t;
public void set(T t) {
this.t = t;
}
public T getData() {
return t;
}
}
Это даст следующий результат.
Вывод
Integer Value :10
Integer Value :10
Integer Value :10
Integer Value :10
Могут быть моменты, когда вы захотите ограничить типы типов, которые разрешено передавать параметру типа. Например, метод, который работает с числами, может хотеть принимать только экземпляры Number или его подклассов. Для этого нужны параметры ограниченного типа.
Чтобы объявить параметр ограниченного типа, укажите имя параметра типа, за которым следует ключевое слово extends и его верхняя граница.
пример
В следующем примере показано, как extends используется в общем смысле для обозначения «расширяет» (как в классах) или «реализует» (как в интерфейсах). Этот пример - универсальный метод для возврата самого большого из трех объектов Comparable -
public class MaximumTest {
// determines the largest of three Comparable objects
public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
T max = x; // assume x is initially the largest
if(y.compareTo(max) > 0) {
max = y; // y is the largest so far
}
if(z.compareTo(max) > 0) {
max = z; // z is the largest now
}
return max; // returns the largest object
}
public static void main(String args[]) {
System.out.printf("Max of %d, %d and %d is %d\n\n",
3, 4, 5, maximum( 3, 4, 5 ));
System.out.printf("Max of %.1f,%.1f and %.1f is %.1f\n\n",
6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ));
System.out.printf("Max of %s, %s and %s is %s\n","pear",
"apple", "orange", maximum("pear", "apple", "orange"));
}
}
Это даст следующий результат -
Вывод
Max of 3, 4 and 5 is 5
Max of 6.6,8.8 and 7.7 is 8.8
Max of pear, apple and orange is pear
Параметр типа может иметь несколько границ.
Синтаксис
public static <T extends Number & Comparable<T>> T maximum(T x, T y, T z)
где
maximum - maximum - это общий метод.
T- Параметр универсального типа, передаваемый универсальному методу. Может принимать любой объект.
Описание
T - это параметр типа, передаваемый в универсальный класс Box, он должен быть подтипом класса Number и должен включать интерфейс Comparable. В случае, если класс передается как связанный, он должен быть передан первым до интерфейса, иначе произойдет ошибка времени компиляции.
пример
Создайте следующую программу Java, используя любой редактор по вашему выбору.
package com.tutorialspoint;
public class GenericsTester {
public static void main(String[] args) {
System.out.printf("Max of %d, %d and %d is %d\n\n",
3, 4, 5, maximum( 3, 4, 5 ));
System.out.printf("Max of %.1f,%.1f and %.1f is %.1f\n\n",
6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ));
}
public static <T extends Number
& Comparable<T>> T maximum(T x, T y, T z) {
T max = x;
if(y.compareTo(max) > 0) {
max = y;
}
if(z.compareTo(max) > 0) {
max = z;
}
return max;
}
// Compiler throws error in case of below declaration
/* public static <T extends Comparable<T>
& Number> T maximum1(T x, T y, T z) {
T max = x;
if(y.compareTo(max) > 0) {
max = y;
}
if(z.compareTo(max) > 0) {
max = z;
}
return max;
}*/
}
Это даст следующий результат -
Вывод
Max of 3, 4 and 5 is 5
Max of 6.6,8.8 and 7.7 is 8.8
Java предоставила общую поддержку в интерфейсе List.
Синтаксис
List<T> list = new ArrayList<T>();
где
list - объект интерфейса List.
T - Параметр универсального типа, переданный при объявлении списка.
Описание
T - это параметр типа, передаваемый в общий интерфейс List и его класс реализации ArrayList.
пример
Создайте следующую программу Java, используя любой редактор по вашему выбору.
package com.tutorialspoint;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class GenericsTester {
public static void main(String[] args) {
List<Integer> integerList = new ArrayList<Integer>();
integerList.add(Integer.valueOf(10));
integerList.add(Integer.valueOf(11));
List<String> stringList = new ArrayList<String>();
stringList.add("Hello World");
stringList.add("Hi World");
System.out.printf("Integer Value :%d\n", integerList.get(0));
System.out.printf("String Value :%s\n", stringList.get(0));
for(Integer data: integerList) {
System.out.printf("Integer Value :%d\n", data);
}
Iterator<String> stringIterator = stringList.iterator();
while(stringIterator.hasNext()) {
System.out.printf("String Value :%s\n", stringIterator.next());
}
}
}
Это даст следующий результат -
Вывод
Integer Value :10
String Value :Hello World
Integer Value :10
Integer Value :11
String Value :Hello World
String Value :Hi World
Java предоставила общую поддержку в интерфейсе Set.
Синтаксис
Set<T> set = new HashSet<T>();
где
set - объект Set Interface.
T - Параметр универсального типа, переданный при объявлении набора.
Описание
T - это параметр типа, передаваемый в универсальный интерфейс Set и его класс реализации HashSet.
пример
Создайте следующую программу Java, используя любой редактор по вашему выбору.
package com.tutorialspoint;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class GenericsTester {
public static void main(String[] args) {
Set<Integer> integerSet = new HashSet<Integer>();
integerSet.add(Integer.valueOf(10));
integerSet.add(Integer.valueOf(11));
Set<String> stringSet = new HashSet<String>();
stringSet.add("Hello World");
stringSet.add("Hi World");
for(Integer data: integerSet) {
System.out.printf("Integer Value :%d\n", data);
}
Iterator<String> stringIterator = stringSet.iterator();
while(stringIterator.hasNext()) {
System.out.printf("String Value :%s\n", stringIterator.next());
}
}
}
Это даст следующий результат -
Вывод
Integer Value :10
Integer Value :11
String Value :Hello World
String Value :Hi World
Java предоставила общую поддержку в интерфейсе карты.
Синтаксис
Set<T> set = new HashSet<T>();
где
set - объект Set Interface.
T - Параметр универсального типа, переданный при объявлении набора.
Описание
T - это параметр типа, передаваемый в универсальный интерфейс Set и его класс реализации HashSet.
пример
Создайте следующую программу Java, используя любой редактор по вашему выбору.
package com.tutorialspoint;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class GenericsTester {
public static void main(String[] args) {
Map<Integer,Integer> integerMap
= new HashMap<Integer,Integer>();
integerMap.put(1, 10);
integerMap.put(2, 11);
Map<String,String> stringMap = new HashMap<String,String>();
stringMap.put("1", "Hello World");
stringMap.put("2","Hi World");
System.out.printf("Integer Value :%d\n", integerMap.get(1));
System.out.printf("String Value :%s\n", stringMap.get("1"));
// iterate keys.
Iterator<Integer> integerIterator = integerMap.keySet().iterator();
while(integerIterator.hasNext()) {
System.out.printf("Integer Value :%d\n", integerIterator.next());
}
// iterate values.
Iterator<String> stringIterator = stringMap.values().iterator();
while(stringIterator.hasNext()) {
System.out.printf("String Value :%s\n", stringIterator.next());
}
}
}
Это даст следующий результат -
Вывод
Integer Value :10
String Value :Hello World
Integer Value :1
Integer Value :2
String Value :Hello World
String Value :Hi World
Знак вопроса (?) Представляет собой подстановочный знак, обозначающий неизвестный тип в универсальных шаблонах. Могут быть моменты, когда вы захотите ограничить типы типов, которые разрешено передавать параметру типа. Например, метод, который работает с числами, может хотеть принимать только экземпляры Number или его подклассов.
Чтобы объявить ограниченный сверху параметр Wildcard, укажите?, За которым следует ключевое слово extends и его верхняя граница.
пример
В следующем примере показано, как extends используется для указания подстановочного знака верхней границы.
package com.tutorialspoint;
import java.util.Arrays;
import java.util.List;
public class GenericsTester {
public static double sum(List<? extends Number> numberlist) {
double sum = 0.0;
for (Number n : numberlist) sum += n.doubleValue();
return sum;
}
public static void main(String args[]) {
List<Integer> integerList = Arrays.asList(1, 2, 3);
System.out.println("sum = " + sum(integerList));
List<Double> doubleList = Arrays.asList(1.2, 2.3, 3.5);
System.out.println("sum = " + sum(doubleList));
}
}
Это даст следующий результат -
Вывод
sum = 6.0
sum = 7.0
Знак вопроса (?) Представляет собой подстановочный знак, обозначающий неизвестный тип в универсальных шаблонах. Могут быть случаи, когда можно использовать любой объект, когда метод может быть реализован с использованием функций, предоставляемых в классе Object, или когда код не зависит от параметра типа.
Чтобы объявить параметр Unbounded Wildcard, укажите? только.
пример
В следующем примере показано, как extends используется для указания неограниченного подстановочного знака.
package com.tutorialspoint;
import java.util.Arrays;
import java.util.List;
public class GenericsTester {
public static void printAll(List<?> list) {
for (Object item : list)
System.out.println(item + " ");
}
public static void main(String args[]) {
List<Integer> integerList = Arrays.asList(1, 2, 3);
printAll(integerList);
List<Double> doubleList = Arrays.asList(1.2, 2.3, 3.5);
printAll(doubleList);
}
}
Это даст следующий результат -
Вывод
1
2
3
1.2
2.3
3.5
Знак вопроса (?) Представляет собой подстановочный знак, обозначающий неизвестный тип в универсальных шаблонах. Могут быть моменты, когда вы захотите ограничить типы типов, которые разрешено передавать параметру типа. Например, метод, который работает с числами, может захотеть принимать только экземпляры Integer или его суперклассов, таких как Number.
Чтобы объявить ограниченный снизу параметр подстановочного знака, укажите?, За которым следует ключевое слово super, за которым следует его нижняя граница.
пример
В следующем примере показано, как super используется для указания подстановочного знака нижней границы.
package com.tutorialspoint;
import java.util.ArrayList;
import java.util.List;
public class GenericsTester {
public static void addCat(List<? super Cat> catList) {
catList.add(new RedCat());
System.out.println("Cat Added");
}
public static void main(String[] args) {
List<Animal> animalList= new ArrayList<Animal>();
List<Cat> catList= new ArrayList<Cat>();
List<RedCat> redCatList= new ArrayList<RedCat>();
List<Dog> dogList= new ArrayList<Dog>();
//add list of super class Animal of Cat class
addCat(animalList);
//add list of Cat class
addCat(catList);
//compile time error
//can not add list of subclass RedCat of Cat class
//addCat(redCatList);
//compile time error
//can not add list of subclass Dog of Superclass Animal of Cat class
//addCat.addMethod(dogList);
}
}
class Animal {}
class Cat extends Animal {}
class RedCat extends Cat {}
class Dog extends Animal {}
Это даст следующий результат -
Cat Added
Cat Added
Подстановочные знаки можно использовать тремя способами:
Upper bound Wildcard-? расширяет Тип.
Lower bound Wildcard-? супер Тип.
Unbounded Wildcard -?
Чтобы решить, какой тип подстановочного знака лучше всего подходит для данного условия, давайте сначала классифицируем тип параметров, передаваемых в метод, как in и out параметр.
in variable- Переменная in предоставляет данные коду. Например, copy (src, dest). Здесь src действует как переменная, являющаяся копируемыми данными.
out variable- Переменная out содержит данные, обновленные кодом. Например, copy (src, dest). Здесь dest действует как переменная, скопировав данные.
Рекомендации по использованию подстановочных знаков.
Upper bound wildcard - Если переменная имеет in категории используйте ключевое слово extends с подстановочным знаком.
Lower bound wildcard - Если переменная имеет out категория, используйте ключевое слово super с подстановочным знаком.
Unbounded wildcard - Если к переменной можно получить доступ с помощью метода класса объекта, используйте несвязанный подстановочный знак.
No wildcard - Если код обращается к переменной в обоих in и out категория, то не используйте подстановочные знаки.
пример
Следующий пример иллюстрирует вышеупомянутые концепции.
package com.tutorialspoint;
import java.util.ArrayList;
import java.util.List;
public class GenericsTester {
//Upper bound wildcard
//in category
public static void deleteCat(List<? extends Cat> catList, Cat cat) {
catList.remove(cat);
System.out.println("Cat Removed");
}
//Lower bound wildcard
//out category
public static void addCat(List<? super RedCat> catList) {
catList.add(new RedCat("Red Cat"));
System.out.println("Cat Added");
}
//Unbounded wildcard
//Using Object method toString()
public static void printAll(List<?> list) {
for (Object item : list)
System.out.println(item + " ");
}
public static void main(String[] args) {
List<Animal> animalList= new ArrayList<Animal>();
List<RedCat> redCatList= new ArrayList<RedCat>();
//add list of super class Animal of Cat class
addCat(animalList);
//add list of Cat class
addCat(redCatList);
addCat(redCatList);
//print all animals
printAll(animalList);
printAll(redCatList);
Cat cat = redCatList.get(0);
//delete cat
deleteCat(redCatList, cat);
printAll(redCatList);
}
}
class Animal {
String name;
Animal(String name) {
this.name = name;
}
public String toString() {
return name;
}
}
class Cat extends Animal {
Cat(String name) {
super(name);
}
}
class RedCat extends Cat {
RedCat(String name) {
super(name);
}
}
class Dog extends Animal {
Dog(String name) {
super(name);
}
}
Это даст следующий результат -
Cat Added
Cat Added
Cat Added
Red Cat
Red Cat
Red Cat
Cat Removed
Red Cat
Обобщения используются для более строгой проверки типов во время компиляции и для обеспечения универсального программирования. Чтобы реализовать универсальное поведение, компилятор java применяет стирание типа. Стирание типа - это процесс, в котором компилятор заменяет общий параметр фактическим методом класса или моста. При стирании типа компилятор гарантирует, что не будут созданы дополнительные классы и не возникнут накладные расходы времени выполнения.
Введите правила стирания
Замените параметры типа в универсальном типе их связанными, если используются параметры ограниченного типа.
Замените параметры типа в универсальном типе на Object, если используются параметры неограниченного типа.
Вставьте отливки типа, чтобы сохранить безопасность типа.
Создайте методы-мосты, чтобы сохранить полиморфизм в расширенных универсальных типах.
Компилятор Java заменяет параметры типа в универсальном типе их связанными, если используются параметры ограниченного типа.
пример
package com.tutorialspoint;
public class GenericsTester {
public static void main(String[] args) {
Box<Integer> integerBox = new Box<Integer>();
Box<Double> doubleBox = new Box<Double>();
integerBox.add(new Integer(10));
doubleBox.add(new Double(10.0));
System.out.printf("Integer Value :%d\n", integerBox.get());
System.out.printf("Double Value :%s\n", doubleBox.get());
}
}
class Box<T extends Number> {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
}
В этом случае компилятор java заменит T классом Number, и после стирания типа компилятор сгенерирует байт-код для следующего кода.
package com.tutorialspoint;
public class GenericsTester {
public static void main(String[] args) {
Box integerBox = new Box();
Box doubleBox = new Box();
integerBox.add(new Integer(10));
doubleBox.add(new Double(10.0));
System.out.printf("Integer Value :%d\n", integerBox.get());
System.out.printf("Double Value :%s\n", doubleBox.get());
}
}
class Box {
private Number t;
public void add(Number t) {
this.t = t;
}
public Number get() {
return t;
}
}
В обоих случаях результат одинаковый -
Вывод
Integer Value :10
Double Value :10.0
Компилятор Java заменяет параметры типа в универсальном типе на Object, если используются параметры неограниченного типа.
пример
package com.tutorialspoint;
public class GenericsTester {
public static void main(String[] args) {
Box<Integer> integerBox = new Box<Integer>();
Box<String> stringBox = new Box<String>();
integerBox.add(new Integer(10));
stringBox.add(new String("Hello World"));
System.out.printf("Integer Value :%d\n", integerBox.get());
System.out.printf("String Value :%s\n", stringBox.get());
}
}
class Box<T> {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
}
В этом случае компилятор java заменит T классом Object, и после стирания типа компилятор сгенерирует байт-код для следующего кода.
package com.tutorialspoint;
public class GenericsTester {
public static void main(String[] args) {
Box integerBox = new Box();
Box stringBox = new Box();
integerBox.add(new Integer(10));
stringBox.add(new String("Hello World"));
System.out.printf("Integer Value :%d\n", integerBox.get());
System.out.printf("String Value :%s\n", stringBox.get());
}
}
class Box {
private Object t;
public void add(Object t) {
this.t = t;
}
public Object get() {
return t;
}
}
В обоих случаях результат одинаковый -
Вывод
Integer Value :10
String Value :Hello World
Компилятор Java заменяет параметры типа в универсальном типе на объект, если используются параметры неограниченного типа, и на тип, если связанные параметры используются в качестве параметров метода.
пример
package com.tutorialspoint;
public class GenericsTester {
public static void main(String[] args) {
Box<Integer> integerBox = new Box<Integer>();
Box<String> stringBox = new Box<String>();
integerBox.add(new Integer(10));
stringBox.add(new String("Hello World"));
printBox(integerBox);
printBox1(stringBox);
}
private static <T extends Box> void printBox(T box) {
System.out.println("Integer Value :" + box.get());
}
private static <T> void printBox1(T box) {
System.out.println("String Value :" + ((Box)box).get());
}
}
class Box<T> {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
}
В этом случае компилятор java заменит T классом Object, и после стирания типа компилятор сгенерирует байт-код для следующего кода.
package com.tutorialspoint;
public class GenericsTester {
public static void main(String[] args) {
Box integerBox = new Box();
Box stringBox = new Box();
integerBox.add(new Integer(10));
stringBox.add(new String("Hello World"));
printBox(integerBox);
printBox1(stringBox);
}
//Bounded Types Erasure
private static void printBox(Box box) {
System.out.println("Integer Value :" + box.get());
}
//Unbounded Types Erasure
private static void printBox1(Object box) {
System.out.println("String Value :" + ((Box)box).get());
}
}
class Box {
private Object t;
public void add(Object t) {
this.t = t;
}
public Object get() {
return t;
}
}
В обоих случаях результат одинаковый -
Вывод
Integer Value :10
String Value :Hello World
При использовании универсальных типов примитивные типы не могут передаваться как параметры типа. В приведенном ниже примере, если мы передадим примитивный тип int классу коробки, компилятор будет жаловаться. Чтобы смягчить то же самое, нам нужно передать объект Integer вместо примитивного типа int.
пример
package com.tutorialspoint;
public class GenericsTester {
public static void main(String[] args) {
Box<Integer> integerBox = new Box<Integer>();
//compiler errror
//ReferenceType
//- Syntax error, insert "Dimensions" to complete
ReferenceType
//Box<int> stringBox = new Box<int>();
integerBox.add(new Integer(10));
printBox(integerBox);
}
private static void printBox(Box box) {
System.out.println("Value: " + box.get());
}
}
class Box<T> {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
}
Это даст следующий результат -
Вывод
Value: 10
Параметр типа нельзя использовать для создания экземпляра объекта внутри метода.
public static <T> void add(Box<T> box) {
//compiler error
//Cannot instantiate the type T
//T item = new T();
//box.add(item);
}
Чтобы добиться такой функциональности, используйте отражение.
public static <T> void add(Box<T> box, Class<T> clazz)
throws InstantiationException, IllegalAccessException{
T item = clazz.newInstance(); // OK
box.add(item);
System.out.println("Item added.");
}
пример
package com.tutorialspoint;
public class GenericsTester {
public static void main(String[] args)
throws InstantiationException, IllegalAccessException {
Box<String> stringBox = new Box<String>();
add(stringBox, String.class);
}
public static <T> void add(Box<T> box) {
//compiler error
//Cannot instantiate the type T
//T item = new T();
//box.add(item);
}
public static <T> void add(Box<T> box, Class<T> clazz)
throws InstantiationException, IllegalAccessException{
T item = clazz.newInstance(); // OK
box.add(item);
System.out.println("Item added.");
}
}
class Box<T> {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
}
Это даст следующий результат -
Item added.
При использовании дженериков параметры типа не могут быть статическими. Поскольку статическая переменная является общей для объекта, компилятор не может определить, какой тип использовать. Рассмотрим следующий пример, если разрешены параметры статического типа.
пример
package com.tutorialspoint;
public class GenericsTester {
public static void main(String[] args) {
Box<Integer> integerBox = new Box<Integer>();
Box<String> stringBox = new Box<String>();
integerBox.add(new Integer(10));
printBox(integerBox);
}
private static void printBox(Box box) {
System.out.println("Value: " + box.get());
}
}
class Box<T> {
//compiler error
private static T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
}
Поскольку и stringBox, и integerBox имеют переменную статического типа с установленным знаком, ее тип не может быть определен. Следовательно, параметры статического типа не допускаются.
Приведение к параметризованному типу не допускается, если оно не параметризовано неограниченными подстановочными знаками.
Box<Integer> integerBox = new Box<Integer>();
Box<Number> numberBox = new Box<Number>();
//Compiler Error: Cannot cast from Box<Number> to Box<Integer>
integerBox = (Box<Integer>)numberBox;
Чтобы добиться того же, можно использовать неограниченные подстановочные знаки.
private static void add(Box<?> box) {
Box<Integer> integerBox = (Box<Integer>)box;
}
Поскольку компилятор использует стирание типа, среда выполнения не отслеживает параметры типа, поэтому во время выполнения разница между Box <Integer> и Box <String> не может быть проверена с помощью оператора instanceOf.
Box<Integer> integerBox = new Box<Integer>();
//Compiler Error:
//Cannot perform instanceof check against
//parameterized type Box<Integer>.
//Use the form Box<?> instead since further
//generic type information will be erased at runtime
if(integerBox instanceof Box<Integer>) { }
Массивы параметризованных типов не допускаются.
//Cannot create a generic array of Box<Integer>
Box<Integer>[] arrayOfLists = new Box<Integer>[2];
Поскольку компилятор использует стирание типа, параметр типа заменяется на Object, и пользователь может добавлять в массив любой тип объекта. И во время выполнения код не сможет выбросить ArrayStoreException.
// compiler error, but if it is allowed
Object[] stringBoxes = new Box<String>[];
// OK
stringBoxes[0] = new Box<String>();
// An ArrayStoreException should be thrown,
//but the runtime can't detect it.
stringBoxes[1] = new Box<Integer>();
Универсальному классу не разрешается прямо или косвенно расширять класс Throwable.
//The generic class Box<T> may not subclass java.lang.Throwable
class Box<T> extends Exception {}
//The generic class Box<T> may not subclass java.lang.Throwable
class Box1<T> extends Throwable {}
Методу не разрешено перехватывать экземпляр параметра типа.
public static <T extends Exception, J>
void execute(List<J> jobs) {
try {
for (J job : jobs) {}
// compile-time error
//Cannot use the type parameter T in a catch block
} catch (T e) {
// ...
}
}
Параметры типа разрешены в предложении throws.
class Box<T extends Exception> {
private int t;
public void add(int t) throws T {
this.t = t;
}
public int get() {
return t;
}
}
Классу не разрешается иметь два перегруженных метода, которые могут иметь одинаковую подпись после стирания типа.
class Box {
//Compiler error
//Erasure of method print(List<String>)
//is the same as another method in type Box
public void print(List<String> stringList) { }
public void print(List<Integer> integerList) { }
}