泛型类型擦除
jdk1.5提供了泛型这一新特性,它是给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住原程序中的非法输入,编译器编译带类型说明的集合时会擦除类型信息,使程序运行效率不受影响。
List<Integer> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
//运行时,list1、 list2被擦除为原始类型ArrayList
System.out.println(list1.getClass());
System.out.println(list2.getClass());
它们的输出结果都为 class java.util.ArrayList.
如果我们试图往list1中添加String类型的数据时,编译器会直接报错,因为list1使用了Integer类型的泛型限定,编译器会检查加入其中的元素是否为Integer类型。
如何绕过编译器
那有没有办法绕过编译器,这样就不会对list1中的类型进行检查,因而添加String类型的数据?
答案是肯定的,通过反射:
list1.getClass().getMethod("add",Object.class).invoke(list1, "abc");//这样编译器就不报错了,而且成功将字符串存入
因为对于参数化的泛型类型,getClass()返回值和原始类型一样,而且编译生成的字节码会进行泛型擦除,只要跳过编译器,就可以往泛型集合中加入其它类型的数据。
参数化类型与原始类型的兼容性
- 参数化类型可以引用一个原始类型的对象,编译器报警告,例如:Collection < String> c = new ArrayList();
- 原始类型可以引用一个参数化类型的对象,编译器报警告,例如:Collection c = new ArrayList< String>();
参数化类型不考虑类型参数的继承关系
- List l = new ArrayList();//错误
- List l = new ArrayList();//错误
–
泛型方法
只有引用类型才能作为泛型方法的实际参数!
private static <T> void swap(T[] a, int i, int j){
T tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
swap(new int[]{1,2,3},1,2);
此时编译器会报错,因为int是基本数据类型,不能作为实际参数传入。
但如果是这样:
private <T> void add(T a, T b){
}
add(1,2);
编译器不会报错,我们可能会说,1,2也是基本数据类型啊,为何就可以?因为这里编译器会自动将其装箱成Integer类型。
多个泛型参数娶最大公约数
private static <T> void f(T a, T b){
}
f(1,"111");
此时T取1跟"111"的最大公约数Object. T此时为Object类型。
–
泛型类
静态方法不能用泛型类的类型作为参数类型!
public class A<E>{
public static void b(E e){
}
}
这样写编译器会报错,b()是静态方法,不能用类的泛型作为参数类型。
获取泛型集合中的元素类型
给定一个泛型集合 List< String> list = new ArrayList<>(); 我们如何获取list中的泛型类型?
我们知道,java编译器编译后会将泛型擦除,因此无法知道其泛型类型
public class A {
public void a(List<String> list){
}
public void a(List<Integer> list){
}
}
像这样,编译器会报错,因为编译器会把 List跟List 看做同一类型,即List.
那么我们怎样获取泛型集合中的元素类型呢?
先给定一个方法,参数就是我们想要知道的泛型集合:
public static void applyList(List< Number> list){
}
通过反射,获取applyList的参数List集合中的具体类型:
//反射得到applyList()方法
Method method = A.class.getMethod("applyList", List.class);
//得到泛型参数类型数组
Type[] types = method.getGenericParameterTypes();
//applyList()中只有一个参数,因此取types[0]
ParameterizedType pType = (ParameterizedType) types[0];
//通过ParameterizedType提供的方法打印类型
System.out.println(pType.getActualTypeArguments()[0]);
结果输出为: class java.lang.Number