一. Java泛型的实现方法: 类型擦除
1. Java的泛型是伪泛型。为什么说Java的泛型是伪泛型呢?因为, 在编译期间,所有的泛型信息都会被擦除掉。正确理解泛型概念的首要前提是理解类型擦出(type erasure)。
2. Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数, 会在编译器在编译的时候去掉。这个过程就称为类型擦除。
3. 如在代码中定义的List和List等类型, 在编译后都会编程List。JVM看到的只是List, 而由泛型附加的类型信息对JVM来说是不可见的。Java编译器会在编译时尽可能的发现可能出错的地方, 但是仍然无法避免在运行时刻出现类型转换异常的情况。类型擦除也是Java的泛型实现方法与C++模版机制实现方式之间的重要区别。
4. 通过例子,来证明java泛型的类型擦除
4.1. 在这个例子中,我们定义了两个LinkedList数组, 不过一个LinkedList泛型类型, 只能存储字符串。另外一个LinkedList泛型类型, 只能存储整形。最后, 我们通过list1对象和list2对象的getClass方法获取它们的类的信息, 最后发现结果为true。说明泛型类型String和Integer都被擦除掉了, 只剩下了原始类型。
public static void main(String[] args) {
LinkedList<String> list1 = new LinkedList<String>();
list1.add("abc");
LinkedList<Integer> list2 = new LinkedList<Integer>();
list2.add(123);
System.out.println(list1.getClass() == list2.getClass());
}
5. 通过例子,来证明java泛型的类型擦除
5.1. 在程序中定义了一个LinkedList泛型类型实例化为Integer的对象, 如果直接调用add方法, 那么只能存储整形的数据。不过当我们利用反射调用add方法的时候, 却可以存储字符串、布尔、浮点数。这说明了Integer泛型实例在编译之后被擦除了, 只保留了原始类型。
public static void main(String[] args) throws Exception {
LinkedList<Integer> list = new LinkedList<Integer>();
list.add(123);
list.getClass().getMethod("add", Object.class).invoke(list, "zhangsan");
list.getClass().getMethod("add", Object.class).invoke(list, true);
list.getClass().getMethod("add", Object.class).invoke(list, 175.3);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
二. 类型擦除后保留的原始类型
1. 原始类型(raw type)就是擦除去了泛型信息, 最后在字节码中的类型变量的真正类型。无论何时定义一个泛型类型, 相应的原始类型都会被自动地提供。类型变量被擦除(crased), 并使用其限定类型(无限定的变量用Object)替换。
2. 泛型实例:
public class Pair<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
3. 原始类型:
public class Pair<Object> {
private Object value;
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
4. 使用javap –c Pari.class 命令查看字节码文件信息:
5. 因为在Pair中, T是一个无限定的类型变量, 所以用Object替换。其结果就是一个普通的类。在程序中可以包含不同类型的Pair, 但是, 擦除类型后它们就成为原始的Pair类型了, 原始类型都是Object。
6. 如果类型变量有限定, 那么原始类型就用第一个边界的类型变量来替换。
6.1. 比如Pair如下声明, 那么原始类型就是Comparable。
public class Pair<T extends Comparable & Serializable> { }