Java - 为什么Java的泛型要用“擦除“实现

前言

在 Java 中的 泛型,常常被称之为 伪泛型,究其原因是因为在实际代码的运行中,将实际类型参数的信息擦除掉了(Type Erasure)。那是什么原因导致了 Java 做出这种妥协的呢?下面我就带着大家以 Java 语言设计者的角度,带领大家一起了解这里面的辛酸过往。

 

什么是真泛型

在了解 Java "伪泛型" 之前,我们先简单讲一讲"真泛型"与“伪泛型”的区别。

  • 真泛型:泛型中的类型是真实存在的。
  • 伪泛型:仅于编译时类型检查,在运行时擦除类型信息。

 

编程语言实现“真泛型”的一般思路

一般编程语言引入泛型的思路,都是通过编译时膨胀法

以 Java 语言实现"真泛型"为例,首先,对泛型类型(泛型类、泛型接口)泛型方法的名字使用特别的编码,例如将 Factory<T> 类生成为一个名为 “Factory@@T” 的类,这种特别的编码后的名字将被编译器识别,作为判断是否为泛型的依据。方法中使用占位符 T 的地方可以临时生成为 Object ,并带上特别的 Annotation 让 Java 编译器能得知这个是占位符。然后,如果编译时发现有对 Factory<String> 的使用,则将 “Factory@@T” 的所有逻辑复制一份,新建 “Factory@String@” 类,将原本的占位符 T 替换为 String 。然后在编译 new Factory<String>() 时生成 new Factory@String@() 即可。

术语 中文含义 举例
Parameterized type 参数化类型 List<String>
Actual type parameter 实际类型参数 String
Formal type parameter 形式类型参数 E

 以 Factory<String> 与 Factory<Integer> 为例,其生成的代码为:

// 替换前的代码
Factory<String>() f1 = new Factory<String>();
Factory<Integer>() f2 = new Factory<Integer>();
 
// 替换后的代码
Factory<String>() f1 = new Factory@String@();
Factory<Integer>() f2 = new Factory@Integer@();
  • 其中 Factory@String@ 类中的 T 已经被替换为 String。
  • 其中 Factory@Integer@ 类中的 T 已经被替换为 Integer。

因为含有不同的 实际类型参数 的 泛型类型 都被替换为了不同的类,且泛型类型中的类型也都得到了确认。所以我们在程序中可以这么做:

class Factory<T> {

    T data;

    public static void f(Object arg){
        if(arg instanceof T){...} // success
        T var = new T(); // success
        T[] array = new T[10]; // success
    }
}

 

Java 直接使用 “真泛型” 带来的问题

单从技术来说,Java 是完全 100% 能实现我们所说的 ”真泛型”。想要实现真泛型,有如下两件事Java 必须要处理:

  • 修改 JVM 源代码,让 JVM 能正确的的读取和校验泛型信息(之前的Java 是没有泛型这种概念的,所以需要修改)。
  • 为了兼容老程序,需为原本不支持泛型的 API 平行添加一套泛型 API(主要是容器类型)。

就拿 ArrayList 来说,也就是必须这么做:

  • java.util.ArrayList
上一篇:设计模式——策略模式(使用反射消除if-else)


下一篇:Spring Bean的实例化