JAVA 泛型擦除法

Java泛型的引入加强了参数类型的安全性,减少了类型的转换。Java 的泛型在编译器有效,在运行期被删除,也就是说所有泛型参数类型在编译后都会被清除掉。Java泛型采用的是擦除法实现的伪泛型,泛型信息(类型变量、参数化类型)编译之后通通被除掉了。使用擦除法的好处就是实现简单、非常容易Backport,运行期也能够节省一些类型所占的内存空间。而擦除法的坏处就是,通过这种机制实现的泛型远不如真泛型灵活和强大。

泛型类型只有在静态类型检查期间才出现,在此之后,程序中的所有泛型类型都将被擦除,替换成它们非泛型上界。

举个例子,我们编写一个Test<T>泛型类:

但是,JVM不知道泛型是什么的。因此,Java使用擦拭法实现泛型,导致了

编译器把类型<T>视为Object;编译器根据<T>实现安全的强制转型。

使用泛型的时候,我们编写的代码也是编译器看到的代码。

所以,Java的泛型是由编译器在编译时实行的,编译器内部永远把所有类型T视为Object处理,但是,在需要转型的时候,编译器会根据T的类型自动为我们实行安全地强制转型。

 

Java 泛型的局限性:

  1. 不能用基本类型实例化类型参数,例如,没有Test<double>,只有Test<Double>,其原因是类型擦除。擦除之后,Pair类含有Object类型的域,而Object不能存储double值。
  2. 运行时类型查询只适用于原始类型。泛型类型经编译器类型擦除后是Object或泛型参数的限定类型(例如Test<T extends Comparable>,Comparable就是T的限定类型,转化后泛型的原始类型就是Comparable,所以Test类不带泛型是Test<Comparable>),即Test类含有Comparable类型的域。
  3. 不能创建参数化类型的数组(泛型数组)。数组存储只会检查擦除后的类型,它可以通过数组存储检查,不过仍会导致一个类型错误,所以不允许创建参数化类型的数组。
  4. 不能实例化类型变量T。不能使用new T(..) , new T[..] 或 T.class 这样的表达式中的类型变量。

例如:

public Test() { start = new T(); } //ERROR!

类型擦除将T改变成Object,调用非本意的new Object()。

  1. 泛型类的静态上下文中类型变量无效。泛型类不能在静态域或静态方法中引用类型变量。
  2. 不能throws或catch泛型类的实例(有关异常)。泛型类继承Throwable类不合法,如public class Problem<T> extends Exception {...} //ERROR 不能通过编译