擦除带来的问题
擦除主要的正当理由是从非泛化代码到泛化代码的转变过程,以及在不破坏现有类库的情况下,将泛型融入Java中。
1.泛型不能用于显式地引用运行时类型的操作之中,例如instanceof、new
class Erased<T>{
public void f(T t,String a){
//T t = new T(); //error
//T[] ts = new T[100]; //error
//boolean k = a instanceof T; //error
}
}
我们可以把具体类型的Class传进来解决部分问题
class Erased<T>{
Class<T> kind;
public Erased(Class<T> kind){
this.kind = kind;
}
public void f(String a) throws Exception{
T t = kind.newInstance(); //这个类需要有默认构造器,反射知识
T[] ts = (T[]) Array.newInstance(kind,10);
boolean k = kind.isInstance(a);
}
}
2.基本类型不能作为参数类型
由于擦除的原因,类型参数会被擦除到上一边界,边界是一个类,不兼容基本类型,所以不能声明List<int>。
Java1.5后有自动包装机制,可以用List<Integer>实现基本类型的泛化。但是要注意int[]与Integer[]是转化的,在一些需要传Integer[]的地方不能传int[].
3.数组
可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象
List<String>[] stringLists; //可以声明带泛型的数组引用
List<String>[] stringLists2 = new ArrayList<String>[1];//不能直接创建带泛型的数组对象
List<String>[] stringLists3 = new ArrayList[1];
可以通过Array.newInstance(Class<T>,int)来创建T[]
4.重载
class Holder<T,E>{
void f(List<T> list){}
void f(List<E> list){}
}
上面的代码将会编译不通过,由于擦除的原因,这两个方法的类型签名是一样的。
5.基类劫持了接口
interface Run<T>{
void with(T t);
}
class Animal implements Run<Integer>{
@Override
public void with(Integer integer) {}
}
public class Dog extends Animal implements Run<String>{}//报错
子类与父类继承用一个泛型接口,如果两个泛型类型不同,将会被父类类型劫持
目录
学会Java泛型系列(零):简介及目录
学会Java泛型系列(一):Java泛型
学会Java泛型系列(二):泛型定义与使用
学会Java泛型系列(三):泛型原理-擦除
学会Java泛型系列(四):擦除带来的问题以及解决办法
学会Java泛型系列(五):限定符
学会Java泛型系列(六):总结
学会Java泛型系列(七):常用案例