学习和理解Java泛型
实现
编译时会对 运行时 泛型类型擦除
编译时类(匿名内部类)中定义的泛型类型是不会被擦除,对应的泛型类型会被保存在Signature中
泛型擦除,只擦除运行时的泛型类型
将动态创建的对象,改为匿名内部类即可获取,匿名内部类是在编译时创建的,泛型类型会保存下来
在生成的Java字节码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会在编译器在编译的时候去掉。
- 原始类型:
1、如果泛型的类型变量没有限定,原始类型默认为Object
2、如果泛型类型有限定(T extends Person),那么原始类型用第一个边界类型Person来替换 使用问题
1、先检查,再编译,以及检查编译的对象和引用传递的问题
编译时擦除类型,所以泛型类型的限定是在编译前检查泛型类型,然后再编译,检查不通过ide会提示错误。
泛型的限定是通过引用来限定,所以定义的引用要有泛型,用引用来操作时,才能限定类型。2、自动类型转换
编译时对泛型类型擦除、转换为原始类型,取出来时也是原始类型,但是在调用处会把取出的原始类型数据根据引用类型强转
ArrayList<Date> list = new ArrayList<>();
list.add(new Date());
Date date = list.get(0);//这里get出来时还是原始类型(Object),但是引用为Date类型,所以会强转成Date类型
可以通过反编译class得到smali代码,看出真正执行过程。3、类型擦除与多态的冲突和解决方法
编译前:
class Pair<T> { private T value; public T getValue() { return value; } public void setValue(T value) { this.value = value; } } class DateInter extends Pair<Date> { @Override public void setValue(Date value) { super.setValue(value); } @Override public Date getValue() { return super.getValue(); } }
编译后(类型擦除后):
class Pair { private Object value; public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } } class DateInter extends Pair<Date> { @Override //编译后子类setValue方法与父类方法参数不同,可见只是重载,而不是重写 public void setValue(Date value) { super.setValue(value); } @Override //编译后子类getValue方法与父类方法返回值不同,可见只是重载,而不是重写 public Date getValue() { return super.getValue(); } }
而实际上子类并没有setValue(Object value)方法,所以子类的setValue(Date date)有重写父类的setValue(Object obj)而该重写的实现比较特殊,由JVM通过桥方法来实现重写的。
反编译class得到的字节码
class com.test.DateInter extends com.test.Pair
编译后的代码实际是这样的:
class DateInter extends Pair<Date> { @Override //编译后子类setValue方法与父类方法参数不同,可见只是重载,而不是重写 public void setValue(Date value) { super.setValue(value); } @Override //编译后子类getValue方法与父类方法返回值不同,可见只是重载,而不是重写 public Date getValue() { return super.getValue(); } //编译时编译器自动生成的桥方法,也是实际重写父类setValue(Object)的方法,该方法内部调用子类的Date getValue() public Object getValue() { return this.getValue(); } //编译时编译器自动生成的桥方法,也是实际重写父类setValue(Object)的方法,该方法内部调用子类的setValue(Date) public void setValue(Object value) { this.setValue((Date)value); } }
这样通过父类pair.setValue(T value)就能够实现多态了
4、泛型类型变量不能是基本数据类型
因为当类型擦除后,泛型限定的类型转为原始类型变为Object,但是Object类型不能存储基本类型的值
5、运行时类型查询
运行时进行类型查询的时候使用 if(list instanceof ArrayList<String>) 的方法是错误的,编译时类型擦除已经转为原始类型了
6、异常中使用泛型的问题
不能抛出也不能捕获泛型类的对象。因为异常都是在运行时捕获和抛出的,而在编译的时候,泛型信息全都会被擦除掉。
7、泛型类型的实例化
1)不能实例化泛型类型、不能建立一个泛型数组,因为类型擦除会使得实例化的对象/数组都是Object/Object[]
2)可以通过反射构造泛型对象和数组public static <T extends Comparable> T[] generateArray(T[] a) { T[] array == (T[])Array.newInstance(a.getClass().getComponentType(),2); //以上代码相当于 Obeject[] mm = new Object[2]; return (T[]) mm; }
8、泛型在静态方法和静态类中的问题
泛型类中的静态方法和静态变量不可以使用泛型类所声明的泛型类型参数
因为泛型类中的【泛型参数的实例化是在定义对象的时候指定的】,而静态变量和静态方法不需要使用对象来调用。
对象都没有创建,如何确定这个泛型参数是何种类型,所以当然是错误的。public class Test<T> { public static T one; //编译错误 public static T show(T one){ //编译错误 return null; } } //下面这个是对的,这是一个泛型方法,在泛型方法中使用的T是自己在方法中定义的T,而不是泛型类中的T public class Test<T> { public static <T> T show(T one){//这是正确的 return null; } }