java-泛型
一、编写泛型
泛型(Generic)一般用在集合类中 编写泛型时,需要定义泛型类型
public class Pair<T> { … }
静态方法不能引用泛型类型
public static <K> Pair<K> create(K first, K last) { … }
泛型可以同时定义多种类型<T, K>
public class Pair<T,K> { … }
二、擦拭法
擦拭法
-
Java的泛型(Generic)是采用擦拭法(Type Erasure)实现的。
-
编译把
视为Object -
编译器根据
实现安全的强制转型
我们写的是
public static void main(String[] args) {
Pair<String> pair = new Pair<>("xiao","ming");
String first = pair.getFirst();
}
实际上编译器处理的是
public static void main(String[] args) {
Pair pair = new Pair("xiao","ming");
String first = (String)pair.getFirst();
}
擦拭法的局限
-
不能是基本类型,例如int -
Object字段无法持有基本类型
-
无法取得带泛型的Class(返回的永远的同一个Class) 如:
Pair<String>.class
-
无法判断带泛型类型的类型,例如:
x instanceof Pair<String>
-
不能实例化 T 类型 如:
new T()
-
可以继承自泛型类:
public class IntPair extends Pair<Integer> {}
-
泛型方法要防止重复定义,例如:
public boolean equals(T obj)
-
子类可以获取父类的泛型类型。
继承关系
三、extends通配符
使用类似<? extends Number>
通配符作为方法参数时
void someMethod(List<? extends Number> list) {
Number n = list.get(0);
list.add(n); // ERROR
}
允许传入List
允许调用方法获取Number类型
不允许调用方法传入Number类型(null除外)
<T extends Number>
定义泛型类
定义泛型时可以通过extends限定T必须是Number或Number的子类
四、super通配符
使用类似<? super Integer>
通配符作为方法参数时表示:
-
允许传入
List<Integer>
,List<Number>
,List<Object>
-
允许调用方法传入Integer类型 set方法
-
不允许调用方法获取Integer类型(Object除外)get方法
void someMethod(List<? super Integer> list) { list.add(123); Integer n = list.get(0); // ERROR }
使用类似
- 泛型限定为Integer或Integer的超类
extends和super通配符的区别
- 允许调用方法获取T的引用
- 允许调用方法传入T的引用
无限定通配符<?>
-
只能获取Object引用
-
只能传入null
-
可以用
消除<?>
五、泛型和反射
部分反射API是泛型:
-
Class
Class clazz = String.class; String str = (String) clazz.newInstance(); Class<String> clazz = String.class; String str = clazz.newInstance(); Class<? super String> sup = clazz.getSuperclass();
-
Constructor
Class<Integer> clazz = Integer.class(); Constructor<Integer> cons = clazz.getConstructor(int.class); Integer i = cons.newInstance(123);
-
可以声明带泛型的数组,但不能直接创建带泛型的数组,必须强制转型
Pair<String> [] ps = null; //正确 Pair<String> [] ps = new Pair<String>[2]; //错误
@suppressWarnings("unchecked") Pair<String>[] ps = (Pair<String>[]) new Pair[2]; //正确
-
不安全的使用带泛型的数组:
public static void main(String[] args) { Pair[] arr = new Pair[2]; Pair<String>[] ps = (Pair<String>[])arr; ps[0] = new Pair<String>("a","b"); arr[1] = new Pair<Integer>(1,2); try { //ClassCastException Pair<String> p = ps[1]; } catch (Exception e) { System.err.println(e.getMessage()); //java.lang.Integer cannot be cast to java.lang.String } }
-
安全的使用带泛型的数组:
@suppressWarnings("") Pair<String>[] ps = (Pair<String>[]) new Pair[2];
-
带泛型的数组实际上是编译器进行类型的擦除
Pair[] arr = new Pair[2]; Pair<String>[] ps = (Pair<String>[]) arr; System.out.println(ps.getClass()==Pair[].class); //true String s1 = (String) arr[0].getFirst(); String s2 = ps[0].getFirst();
-
可以通过Array.newInstance(Class
, int)创建T[]数组,需要强制转型 -
不能直接创建T[]数组,擦拭后代码变为new Object[]
//compile error: public class Abc<T> { T[] createArray() { return new T[5]; } }
-
借助Class
public class Abc<T>{ T[] createArray(Class<T> cls) { return (T[])Array.newInstance(cls,5); } }
//使用 String[] s = new Abc<String>().createArray(String.class);
-
利用可变参数创建T[]数组,@SafeVarargs消除编译警告
public class ArrayHelper { @SafeVarargs static <T> T[] asArray(T... objs){ return objs; } public static void main(String[] args) { String[] ss = asArray("a","B","c"); } }