泛型擦除:
阅读下面之前先理解泛型,这样有利于加强理解。
1.泛型擦除概念:在JVM中一切关于对T的传参都不再有效,都将被向上转译成相应的其他类型,何为向上转译,且听我细细道来:可以代表任意类类型,但任意类类型的直接父类都是Object,而<T extends 类类型>的直接父类是<T extends 类类型>中的类类型,这就是所谓的向上转译。
2.泛型擦除规则:
对于没有继承的T(其他字母代替也可以,这没有特别要求),即擦除后转译为Object类型,但如果指定了上限,即<T extends 类类型>转译为类类型,例如则转译为Integer类型。
3.下面我们来通过demo来理解泛型擦除:
System.out.println("------------------------No.6_1泛型类型擦除例子 :--------------------------------------------");
List<String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println("泛型擦除 : "+(l1.getClass()==l2.getClass()));
测试结果:
分析:l1对应的类是String,l2对应的类是Integer,答案为什么是true?原因是:List和List l2在JVM中都是List.class,也就是说List和List l2都没了,用专业术语来讲是类型擦除,至于List和List l2是怎么转换成List.class(泛型转译)的呢?这时我们就可以使用反射机制来动态获取类信息了:
3_1.首先先获取类信息:
Et<String> etTest12 = new Et<String>("字符串类型");
Class etTest12_1 = etTest12.getClass();
System.out.println("Et class is : "+etTest12_1.getName());
3_2.再利用反射获取属性:
Field[] fi = etTest12_1.getDeclaredFields();
for(Field fp:fi) {
System.out.println("Field name : "+fp.getName()+" type :"+fp.getType().getName());
}
看吧,属性key的类型是Object,与我们上面的规则不矛盾,perfect,
3_3.然后我们将类的参数改为 :public class Et <T extends String>
然后重复执行3_2demo,结果如下:很完美,跟规则一模一样,
3_4.既然我们能用反射获取属性的类型,我们也能够以此来获取方法中参数和方法的类型,如下:
Method[] me = etTest12_1.getDeclaredMethods();
for(Method mp:me) {
System.out.println("Method : "+mp.toString());
}
看吧,完全在意料之中,方法类型java.lang.Object,参数类型也是java.lang.Object。
来到这里,我们不仅明白了泛型擦除的真谛,也明白了反射能够对擦除的属性和方法进行操作,基于这一点,我们能够利用固定传参后的类进行不同数据类型的值的存取。如下:List<Integer> l3 = new ArrayList<Integer>();
已经固定是String类型了,但通过反射调用擦除的add()方法实现了对字符串,双精度等等类型的值的存储。
System.out.println("------------------------No.6_2利用泛型类型擦除和反射绕过泛型限制的例子 :--------------------------------------------");
List<Integer> l3 = new ArrayList<Integer>();
l3.add(123);
try {
//注意方法名和参数类型(Object.class)
Method method = l3.getClass().getDeclaredMethod("add", Object.class);
try {
//调用add()方法
method.invoke(l3, "test");
method.invoke(l3, 1024.857);
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for(Object l3_1:l3) {
System.out.println(l3_1);
}
}