泛型的实现原理


注:本篇文章更多只是对网上信息的个人总结,主要是为了留存笔记。如有侵权请联系本人删除。

JDK1.5推出时,为了保证向后兼容,选择的泛型实现方法是擦拭机制。

擦除机制是Java5用来实现泛型的技术。一般来说,在运行时阶段,Java编译器先执行类型检查,然后执行擦除或删除泛型信息。在程序运行的整个阶段中,虚拟机对泛型一无所知,所有的工作都是编译器做的。

在我们使用带泛型的ArrayList时,编译器能看见的代码类似下方这样:

public class ArrayList {
    private T[] array;
    private int size;
    public void add(T e) {...}
    public void remove(int index) {...}
    public T get(int index) {...}
}

编译器处理完毕后交给JVM的代码类似下方这样:

public class ArrayList {
    private Object[] array;
    private int size;
    public void add(Object e) {...}
    public void remove(int index) {...}
    public Object get(int index) {...}
}

编译器总是将转为Object,并且在需要转型的时候,编译器会根据T的类型自动为我们实行安全地强制转型。

ArrayList list = new ArrayList();
list.add("hello world");
String hello = (String) list.get(0);

擦拭机制的特性可以知道,泛型有以下局限:

  1. 不能是基本类型,例如int,因为实际类型是Object,Object类型无法持有基本类型

  2. 无法取得带泛型的Class

    对ArrayList和ArrayList类型获取Class时,获取到的是同一个Class,也就是ArrayList类的Class。也就是说,所有泛型实例,无论T的类型是什么,getClass()返回同一个Class实例,因为编译后它们全部都是ArrayList

  3. 无法判断带泛型的类型

    ArrayList array = new ArrayList<>("hello");
    // Compile error:
    if (array instanceof ArrayList) {
        ...
    }
    

    原因和前面一样,并不存在ArrayList.class,而是只有唯一的ArrayList.class。

  4. 不能实例化T类型

    对于类型参数T,不能“new T()”是因为,并不是每个类型都有一个空的构造器。Java是静态类型语言,从好的或坏的方面来说,我们不能简单的调用某一类型的空构造器,因为这个类型自身并不静态地知道有这样的构造器存在。