擦除的神秘之处

当你开始更深入地专研泛型时,会发现有大量的东西初看起来是没有意义的。例如,尽管可以声明ArrayList.class
,但是不能声明 ArrayList.class 。请考虑下面的情况:

  1. //:generics/ErasedTypeEquivalence.java
  2. import java.util.*;
  3. public class ErasedTypeEquivalence {
  4. public static void main(String[] args) {
  5. Class c1 = new ArrayList<String>().getClass();
  6. Class c2 = new ArrayList<Integer>().getClass();
  7. System.out.println(c1 == c2);
  8. }
  9. } /* Output:
  10. true
  11. *///:~

ArrayList ArrayList 很容易被认为是不同的类型。不同的类型在行为方面肯定不同,例如,如果尝试着将一个 Integer 放入 ArrayList , 所得到的行为(将失败)与把一个 Integer 放入
ArrayList(将成功)所得到的行为完全不同。但是上面的程序会认为它们是相同的类型。

在泛型代码内部,无法获得任何有关泛型参数类型的信息。**

Java泛型是使用擦除来实现的,这意味着当你在使用泛型时,任何具体的类型信息都被擦除了,你唯一知道的就是你在使用一个对象。因此 List List 在运行时事实上是相同的类型。

为什么要擦除?(迁移兼容性)

擦除是 Java 的泛型实现的一种折中,因为泛型不是Java语言出现时就有的组成部分,所以这种折中是必需的。

在基于擦除的实现中,泛型类型被当作第二类类型处理,即不能在某些重要的上下文环境中使用的类型。泛型类型只有在静态类型检查期间才出现,在此之后,程序中的所有泛型类型都将被擦除,替换为它们的非泛型上界。例如,诸如 List 这样的类型注解将被擦除为 List ,而普通的类型变量在未指定边界的情况下将被擦除为 Object。
**
擦除的核心动机是它使得泛化的客户端可以用非泛化的类库来使用,反之亦然,这经常被称为“迁移兼容性”。

擦除的问题

擦除的代价是显著的。泛型不能用于显式地引用运行时类型的操作之中,例如转型、instanceof 操作和 new 表达式。因为所有关于参数的类型信息都丢失了。

任何基本类型都不能作为类型参数
不能创建参数化类型的数组
不能实例化类型变量
不能构造泛型数组

推荐阅读

Java泛型类型擦除以及类型擦除带来的问题