1.Java泛型的实现方法:类型擦除
Java语言的泛型实现方式是擦拭法(Type Erasure)。
所谓擦拭法是指,虚拟机对泛型其实一无所知,所有的工作都是编译器做的。Java的泛型是由编译器在编译时实行的,编译器内部永远把所有类型T视为Object处理,但是,在需要转型的时候,编译器会根据T的类型自动为我们实行安全地强制转型。
Java 的泛型是伪泛型,因为在编译期间所有的泛型信息都会被擦除掉,泛型参数保留为原始类型。譬如 List 在运行时仅用一个 List 来表示(所以我们可以通过反射 add 方法来向 Integer 的泛型列表添加字符串,因为编译后都成了 Object),这样做的目的是为了和 Java 1.5 之前版本进行兼容。
泛型擦除具体来说就是在编译成字节码时首先进行类型检查,接着进行类型擦除(即所有类型参数都用他们的限定类型替换,包括类、变量和方法上的泛型参数,如果类型变量有限定则原始类型就用第一个边界的类型来替换,譬如
class Prd<T extends Comparable & Serializable> {} 的原始类型就是
Comparable),接着如果类型擦除和多态性发生冲突时就在子类中生成桥方法解决,接着如果调用泛型方法的返回类型被擦除则在调用该方法时插入强制类型转换。
2.类型擦除后保留的原始类型
原始类型 就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型,类型变量擦除,并使用其限定类型(无限定的变量用Object)替换。
如果类型变量有限定,那么原始类型就用第一个边界的类型变量类替换。
比如: Pair这样声明的话
public class Pair<T extends Comparable> {}
那么原始类型就是Comparable。
3.自动类型检查
Java编译器是通过先检查代码中泛型的类型,然后再进行类型擦除,再进行编译。
4.自动类型强制转换
因为类型擦除的问题,所以所有的泛型类型变量最后都会被替换为原始类型。
既然都被替换为原始类型,那么为什么我们在获取的时候,不需要进行强制类型转换呢?
看下ArrayList.get()方法:
public E get(int index) {
RangeCheck(index);
return (E) elementData[index];
}
可以看到,在return之前,会根据泛型变量进行强转。假设泛型类型变量为Date,虽然泛型信息会被擦除掉,但是会将(E) elementData[index],编译为(Date)elementData[index]。所以我们不用自己进行强转。当存取一个泛型域时也会自动插入强制类型转换。
自动地在结果字节码中插入强制类型转换。