类型擦除
一 泛型
在JAVA中的泛型,在编译的时候,所有的泛型信息都会被抹去。这个过程成为
类型擦除。这样做的原因是为了兼容老版本。JAVA中泛型的引入主要是为了解决两个方面的问题:
- 减少类型转换
- 解决的时重复代码的编写,能够复用算法,泛化。
以类Dad为例,有一成员是泛型,并且有对应的get,set方法。
1 | public class Dad<T> { |
而在反编译后,代码如下,可以看到类型T全部被Object替换。如果定义的类泛型指定为 <T extend Comparable>,则编译后所有的T替换为Comparable
1 | public class Dad{ |
二 类型擦除产生的特性
1 编译检查根据引用确定:
既然编译后类型变为Object,因此赋值的时候传递不同类型的值是否可以呢?答案是不可以,因为编译器在编译前会先检查代码中泛型的类型,不符合条件编译不通过。
1 | //正确方式 |
而如下这种方式虽然可以通过编译但是会有编译警告,但我们这么用就没意义了,因为存储的类型实际上是Object,如果要强行转换为String是会出现 类型转换异常的。
1 | List list = new ArrayList<String>(); |
从上面的例子可以看出,编译器在检查泛型是否合格的时候确实是根据
引用检查的
2 不允许引用传递时改变类型:
既然前面我们知道了编译器检查泛型是否合格是根据 引用来决定是否通过的,那么我们引用传递下换成不同的类型是否是可以的呢? 答案是不可以的,泛型的出现就是为了尽量减少类型转换,这样写代码也就失去了意义。
1 | List<Object> list1 = new ArrayList<>(); |
3 集合会自动类型转换
因为在编译的时候都变为了Object,而我们获取后为何还是我们制定的类型呢?以ArrayList为例,在获取元素前已经做了类型转换,我们不再需要进行类型转换了。
1 | E elementData(int index) { |
4 桥方法解决与多态的冲突
有一个泛型父类如下:
1 | public class Dad<T> { |
有子类继承它,并且指定泛型类型,我们会发现这样写 @Override注解是通过的,也就是满足重写。但是父类编译后是Object,而我们子类的两个方法是String类型,因为方法重写的规则是 方法的参数是必须是相同类型的,因此这里的set方法其实并不满足重写规则的,
1 | public class Sub extends Dad<String>{ |
将子类反编译后会发现,多出来两个对应的方法。可以看到这两个方法调用了我们重写的方法,实际上这两个方法才满足 @Override,这就是 桥方法,编译后通过这个方法解决了泛型类重写的问题。
1 | public class Sub extends Dad { |
5 泛型类型变量不能是基本数据类型
因为类型擦除后所有的泛型关键字都是要替换成Object或者其子类,反正一定要是引用类型。如果要使用基本类型存储数据可以使用相应的包装类。
6 集合的instanceof编译不通过
因为编译后类型被擦除,无论是 List<String>还是 List<Integer>都变成了 List,因此下面的语句在编译时候是不通过的:
1 | ArrayList<String> arrayList = new ArrayList<String>(); |
7 静态成员或者方法无法声明为泛型
因为泛型类型是创建对象的时候才确定是什么类型的,而静态属性或者方法不需要使用对象调用,无法确定泛型是什么类型的。
而下面这种情况例外:因为方法show的返回值类型是由方法参数决定的,返回值类型就是参数类型。
1 | public class Demo<T> { |
三 如何保存泛型信息
既然泛型信息擦除了,那么反射应该是获取不到类型信息的吧,但是还是能获取到
有一个泛型父类,在构造方法中通过反射获取类型信息
1 | public class EntityHandler<T> { |
子类赋值为了User类型
1 | public class UserHandler extends EntityHandler<User>{ |
控制台打印:
1 | test.base.generic.EntityHandler<test.base.generic.User> |
答案就是在编译为字节码文件的时候,泛型信息通过 Signature保存了下来
1 | { |
1 | List<String> l1 = new ArrayList<>(); |
- 标题: 类型擦除
- 作者: chalmery
- 创建于 : 2021-10-02 00:00:00
- 更新于 : 2021-10-02 00:00:00
- 链接: https://github.com/chalmery/2260828129/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。