类型擦除的概念是我们在初步学习Java泛型首先要了解的,这对我们后面的深入学习有很重要的意义,由浅入深,循序渐进,我们学习起来也能事半功倍。
泛型这种语法糖,编译器会在编译期间「擦除」泛型语法并相应的做出一些类型转换动作。例如:
public class Caculate<T> {
private T num;
}
我们定义了一个泛型类,具体定义泛型类的细节待会会进行详细介绍,这里关注我们的类型擦除过程。定义了一个属性成员,该成员的类型是一个泛型类型,这个 T 具体是什么类型,我们也不知道,它只是用于限定类型的。
当然,我们也可以反编译一下这个 Caculate 类:
public class Caculate{
public Caculate(){}
private Object num;
}
会得到这样一个结果,很明显的是,编译器擦除 Caculate 类后面的两个尖括号,并且将 num 的类型定义为 Object 类型。
当然,有人可能就会问了,「是不是所有的泛型类型都以 Object 进行擦除呢?」
答案是:大部分情况下,泛型类型都会以 Object 进行替换,而有一种情况则不是。
public class Caculate<T extends String> {
private T num;
}
这种情况的泛型类型,num 会被替换为 String 而不再是 Object。
这是一个类型限定的语法,它限定 T 是 String 或者 String 的子类,也就是你构建 Caculate 实例的时候只能限定 T 为 String 或者 String 的子类,所以无论你限定 T 为什么类型,String 都是父类,不会出现类型不匹配的问题,于是可以使用 String 进行类型擦除。
那么很多人也会有这样的疑问,你类型擦除之后,所有泛型相关方法的返回值都是 Object,那我当初泛型限定的具体类型还有用吗?例如这样一个方法:
ArrayList<Integer> list = new ArrayList();
list.add(10);
Integer num = list.get(0);
//这是 ArrayList 内部的一个方法
public E get(int index) {
.....
}
就是说,你类型擦除之后,方法 get 的返回值 E 会被擦除为 Object 类型,那么为什么我们看到的确实返回的 Integer 类型呢?
这是上述三行代码的一个反编译结果,可以看到,实际上编译器会正常的将 ArrayList 编译并进行类型擦除,然后返回实例。但是除此之外的是,如果构建 ArrayList 实例时使用了泛型语法,那么编译器将标记该实例并关注该实例后续所有方法的调用,每次调用前都进行安全检查,非指定类型的方法都不能调用成功。
其实还有一点可能大家都很少关注,大多数人只是知道编译器会类型擦除一个泛型类并对创建出来的实例进行一定的安全检查。但是实际上编译器不仅关注一个泛型方法的调用,它还会为某些返回值为限定的泛型类型的方法进行强制类型转换,由于类型擦除,返回值为泛型类型的方法都会擦除成 Object 类型,当这些方法被调用后,编译器会限定这个结果只能赋值给 Integer 或者 Object。
其实这一个过程,我们管它叫做『泛型翻译』。类似的专业名词我们在继续学习泛型中还会遇到,有不懂的地方可以在网上查阅资料,也可以到动力节点在线发布问答需求,我们的在线老师会为你找出满意的答案。
提枪策马乘胜追击04-21 20:01
代码小兵92504-17 16:07
代码小兵98804-25 13:57
杨晶珍05-11 14:54