1、什么是擦除
java的泛型是一种伪泛型,对于类型参数T,如果没有边界则擦除到Object,如果有边界则擦除到第一个边界。对于泛型类型则擦除为原生类型。
边界:对泛型的参数类型上设置限制条件,例如重用extends关键字将泛型参数类型限定为某个类的子类<T extends SuperClass>那么SuperClass为T的一个边界
体现类型擦除的例子
Class<T>被擦除为Class
/* 一个利用反射产生数组的类 */
class ArrayMaker<T>{
priavte Class<T> kind;
public ArrayMaker(Class<T> kind){
this.kind =kind;
}
T[] create(int size){
//这里会有警告,因为Class<T>被擦除为Class,所以这个函数
//并不能确定kind的具体类型
return (T[]) Array.newInstance(kind,size);
}
}
泛型参数T被擦除为HasF,即它的第一个边界
class HasF{
public void f(){};
}
public class Test<T extends HasF>{
private T obj;
public Test(T x){ obj = x;}
//若没有extends HasF则会报错,实际上T被擦除为HasF
public void g(){ obj.f();}
public static void main(String[] args){
Test<HasF> t = new Test<>(new HasF());
}
}
编译器保证类型的一致性
即如果泛型类Test<T,U>有两个泛型参数,虽然他们的类型信息都被擦除了,但是编译器可以识别T和U是不同的类型
2、什么时候进行擦除
擦除都发生在边界处。对于传给类型参数的值,在传进来时只是进行了编译期的检查;对于传递出去的值是在传递出去时加入了转型。这里的边界就是传进来和传出去时。
一个例子
//这个例子是,在Holder类中有一个私有域obj,类提供设置私有域和获取私有的方法set()和get()
//首先是使用Object替代泛型写法
public class SimplerHolder{
private Object obj;
public void set(Object obj) {this.obj=obj;}
public Object get() {return obj;}
public static void main(String[] args){
SimpleHolder holder = new SimpleHolder();
holder.set("Item");
String s =(String) holder.get();//这里需要转型
}
}
//然后是泛型的写法
public class GenericHolder<T>{
private T obj;
public void set(T obj) {this.obj=obj;}
public T get() {return obj;}
public static void main(String[] args){
GenericHolder<String> holder = new GenericHolder<>();
holder.set("Item");//传递进去的值
String s = holder.get();//传递出来的值
}
}
通过反编译上面两个类可以发现他们的字节码完全相同,即在使用set()方法时都是编译器执行类型检查,对于get()方法使用非泛型实现时是手动加了(String)进行转型,而使用泛型实现时是编译器帮你插入了(String)这个转型代码,两者并无区别。