在 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 是完全 100%
能实现我们所说的 ”真泛型”。想要实现真泛型,有如下两件事Java 必须要处理:
就拿 ArrayList 来说,也就是必须这么做: