мой блогПожалуйста, указывайте первоисточник при перепечатке.
последовательность
Причина, по которой я хочу написать о дженериках, заключается в том, что я видел такой фрагмент кода:
В то время мои мысли были такими:
Поэтому я быстро просмотрел его и записал. Фундамент непрочен, и исходный код понять невозможно.
Введение в дженерики
Универсальные шаблоны Java (дженерики) — это новая функция, представленная в JDK 5. Универсальные шаблоны предоставляют механизм безопасного обнаружения типов во время компиляции, который позволяет программистам обнаруживать недопустимые типы во время компиляции. Суть дженериков заключается в параметризованном типе, что означает, что тип данных, с которым нужно манипулировать, указывается в качестве параметра, что широко используется в структуре коллекций Java.
Смысл определения в том, чтобы обеспечить механизм обнаружения безопасности типа во время компиляции. Например, есть такой общий класс:
public class Generics <T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
Затем напишите такой класс:
public class Generics {
private Object value;
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
Они также могут хранить все значения, но универсальные классы имеют проверки безопасности типа во время компиляции:
универсальный класс
Класс, который определяет одну или несколько переменных типа, является универсальным классом. Синтаксис заключен в угловые скобки после имени класса, а переменные типа записываются внутри через запятую. Затем вы можете использовать переменные типа в возвращаемом типе, параметрах и полях метода, а также в локальных переменных, но не вstatic
Модификатор используется в измененном методе или поле. пример:
类定义参考上文例子
使用形式
Generics<String> generics = new Generics<String>();
后面尖括号内的内容在Jdk7以后可以省略
Generics<String> generics = new Generics<>();
общий метод
Метод, который определяет одну или несколько переменных типа, является универсальным методом. Синтаксис заключен в угловые скобки после модификатора метода и перед возвращаемым типом, а переменные типа записываются внутри и разделяются запятыми. Универсальные методы могут быть определены в обычных классах и универсальных классах, а универсальные методы могут бытьstatic
модификатор модификатор.
пример:
private <U> void out(U u) {
System.out.println(u);
}
调用形式,
Test.<String>out("test");
大部分情况下<String>都可以省略,编译器可以推断出来类型
Test.out("test");
Ограничения на переменные типа
Иногда у нас бывают ситуации, когда мы хотим ограничить переменные типа, например, ограничить переменные указанного типа, которые необходимо реализовать.List
интерфейс, так что мы можем вызвать переменную типа в кодеList
Метод в интерфейсе, не беспокоясь об отсутствии этого метода.
public class Generics <T extends List> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
public void add(Object u) {
value.add(u);
}
public static void main(String[] args) {
Generics<List> generics = new Generics<>();
generics.setValue(new ArrayList<>());
generics.add("ss");
System.out.println(generics.getValue());
}
}
Квалифицированный синтаксис должен добавить переменную типа послеextends
ключевое слово, а затем добавьте квалифицированный тип, следует использовать несколько квалифицированных типов.&
разделены. Переменные типа и ограниченные типы могут быть классами или интерфейсами, потому что класс в Java может наследовать только один класс, поэтому, если ограниченный тип является классом, он должен быть первым в ограниченном списке.
стирание типа
Стирание типа создается для совместимости, в том смысле, что в виртуальной машине нет универсального типа, а универсальный тип существует только во время компиляции. Переменные универсального типа стираются после компиляции и заменяются первым квалифицированным типом (используется неквалифицированный тип).Object
заменять). вышесказанноеGenerics <T>
После стирания универсального класса создается соответствующий примитивный тип:
public class Generics {
private Object value;
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
Причина, по которой мы можем установить и вернуть правильный тип, заключается в том, что компилятор автоматически вставляет инструкции по преобразованию типов.
public static void main(String[] args) {
Generics<String> generics = new Generics<>();
generics.setValue("ss");
System.out.println(generics.getValue());
}
javac Generics.java
javap -c Generics
编译后的代码
public static void main(java.lang.String[]);
Code:
0: new #3 // class generics/Generics
3: dup
4: invokespecial #4 // Method "<init>":()V
7: astore_1
8: aload_1
9: ldc #5 // String ss
11: invokevirtual #6 // Method setValue:(Ljava/lang/Object;)V
14: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
17: aload_1
18: invokevirtual #8 // Method getValue:()Ljava/lang/Object;
21: checkcast #9 // class java/lang/String
24: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
27: return
Мы видим, что инструкция преобразования типа вставлена в строку 21.
Стирание типа также приносит еще одну проблему, если у нас есть класс, который наследует универсальный класс и переопределяет метод родительского класса:
public class SubGenerics extends Generics<String> {
@Override
public void setValue(String value) {
System.out.println(value);
}
public static void main(String[] args) {
Generics<String> generics = new SubGenerics();
generics.setValue("ss");
}
}
Из-за стирания типаSubGenerics
На самом деле их дваsetValue
метод,SubGenerics
мой собственныйsetValue(String value)
метод и изGenerics
унаследовалsetValue(Object value)
метод. в примереgenerics
ЦитируетсяSubGenerics
объект, поэтому то, что мы хотим назвать,SubGenerics.setValue
. Для обеспечения правильного полиморфизма компиляторSubGenerics
класс сгенерировал桥方法
:
public void setValue(Object value) {
setValue((String) value);
}
Мы можем скомпилировать и проверить:
Compiled from "SubGenerics.java"
public class generics.SubGenerics extends generics.Generics<java.lang.String> {
public generics.SubGenerics();
Code:
0: aload_0
1: invokespecial #1 // Method generics/Generics."<init>":()V
4: return
public void setValue(java.lang.String);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_1
4: invokevirtual #3 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
7: return
public static void main(java.lang.String[]);
Code:
0: new #4 // class generics/SubGenerics
3: dup
4: invokespecial #5 // Method "<init>":()V
7: astore_1
8: aload_1
9: ldc #6 // String ss
11: invokevirtual #7 // Method generics/Generics.setValue:(Ljava/lang/Object;)V
14: return
public void setValue(java.lang.Object);
Code:
0: aload_0
1: aload_1
2: checkcast #8 // class java/lang/String
5: invokevirtual #9 // Method setValue:(Ljava/lang/String;)V
8: return
}
Цитата из "Java Core Technology Volume 1"
В заключение необходимо помнить о фактах об общих преобразованиях Java: 1. В виртуальной машине нет дженериков, только обычные классы и методы. 2. Все параметры типов заменяются их уточненными типами. 3. Метод моста синтезируется для сохранения полиморфизма. 4. Чтобы обеспечить безопасность типов, интерполируйте приведения, когда это необходимо.
Ограничения и ограничения
- Переменные типа не могут быть примитивными переменными, такими как
int,double
Ждать. следует использовать их класс-оболочкуInteger,Double
. - В виртуальной машине нет дженериков, поэтому вы не можете использовать запросы типа времени выполнения, такие как
if (generics instanceof Generics<String>) // Error
- Массивы общих классов не могут быть созданы из-за стирания типа, например
Generics<String>[] generics = new Generics<String>[10]; // Error
- Переменные типа не могут быть созданы, например
new T(...) new T[...] 或 T.class
подстановочный знак
Тип подстановочного знака чем-то похож на ограничение переменной типа выше, разница в том, что тип подстановочного знака используется во время объявления, а ограничение переменной типа — во время определения. такие как подстановочные знакиGenerics<? extends List>
представляет любой общийGenerics
Типовая переменная типаList
а такжеList
подкласс .
Generics<? extends List> generics = new Generics<ArrayList>();
Но после этого заявленияGenerics
Метод также изменился, став
Это делает невозможным вызовsetValue
метод
а такжеgetValue
метод нормальный
квалификация супертипа
Квалификация с подстановочными знаками также может квалифицировать суперклассы, например типы с подстановочными знаками.Generics<? super ArrayList>
представляет любой общийGenerics
Типовая переменная типаArrayList
а такжеArrayList
суперкласс.
Generics<? super ArrayList> generics = new Generics<List>();
такой же,Generics
Метод также изменился, став
перечислитьgetValue
методы могут быть назначены толькоObject
Переменная
перечислитьsetValue
метод может быть передан только вArrayList
а такжеArrayList
подкласс, суперклассList,Object
не могу дождаться
отражение и дженерики
Хотя в виртуальной машине нет дженериков из-за стирания типов. Однако стертый класс по-прежнему сохраняет некоторую информацию о дженериках и может использовать связанные с отражениемApi
чтобы получить.
Точно так же взгляните на общие методы
public static <T extends Comparable<? super T>> T min(T[] a)
это после стирания
public static Comparable min(Coniparable[] a)
API отражения можно использовать для определения:
- Этот общий метод имеет метод, называемый
T
параметр типа. - Этот параметр типа имеет квалификацию подтипа и сам является универсальным типом.
- Этот квалифицированный тип имеет подстановочный параметр.
- Этот подстановочный знак имеет квалификацию супертипа.
- Этот общий метод имеет общий параметр массива.
постскриптум
Черновик был создан в понедельник, а писался только в воскресенье, а некоторые подразделы были удалены.Боюсь, что прокрастинация запоздала... Но это еще и потому, что общего содержания достаточно, хотя в повседневных делах его мало Пишите код, связанный с дженериками, сами, но если вы не понимаете дженериков при чтении исходного кода библиотеки классов, вам будет сложно что-либо сделать, особенно связанное с коллекциями. Большая часть контента на этот раз находится в "Java Core Technology Volume 1", который посвященJava
Хорошая книга для основ. Однако это все еще старое правило, его недостаточно просто прочитать, нужно еще записать на своем родном языке. Как мы все знаем, сущность человека — это повторение. Повторение содержания хорошей книги означает, что я также несу ответственность!