这更多的是关于Java类型系统中原始类型和泛型类型的混合,而不是类型擦除。让我从问题中补充代码片段:
ArrayList<Integer> ar = new ArrayList<Integer>();
List l = new ArrayList(); // (1)
l.add("a");
l.add("b");
ar.addAll(l); // (2)
System.out.println(ar);
Integer i = ar.get(0); // (3)
使用今天擦除的泛型,行(3)抛出。如果重新定义了 Java 的泛型,则很容易假设运行时类型检查会导致在第 (2) 行引发异常。这将是一种可能的仿制药设计,但其他设计可能不会进行这种检查。为什么不呢?主要出于同样的原因,我们今天删除了泛型:迁移兼容性。ClassCastException
Neal Gafter在他的文章Reified Generics for Java中观察到,泛型有很多不安全的使用,转换不正确,等等。今天,即使在泛型被引入十多年后,我仍然看到大量原始类型的使用。(不幸的是,包括Stack Overflow。无条件地执行重新定义的泛型类型检查会破坏大量代码,这当然会对兼容性造成很大的打击。
任何现实的通用再化提案都必须在选择加入的基础上提供再化,例如通过子类型(如Gafter的提案)或通过注释(Gerakios,Biboudis,Smaragdakis)。使用 Java 注释的已初始化类型参数。[PDF]GPSE 2013),它必须决定如何处理原始类型。完全禁止原始类型似乎是完全不切实际的。反过来,有效地允许原始类型意味着有一种方法可以规避泛型类型系统。
(这种决定不是轻率作出的。我目睹了类型理论家之间的激烈匹配,其中一位抱怨Java的类型系统不健全。对于类型理论家来说,这是最严重的侮辱。
从本质上讲,这就是此代码的作用:它通过使用原始类型绕过泛型类型检查好坏处。即使重新定义了Java的泛型,检查也可能不在第(2)行完成。在一些已初始化的泛型设计下,代码的行为可能与今天完全相同:在第 (3) 行引发异常。
在Jon Skeet的回答中,他承认有点惊讶,在第(2)行,编译器信任该列表包含正确类型的元素。这实际上与信任无关 - 毕竟,编译器确实在这里发出警告。更多的是编译器说,“好吧,你使用的是原始类型,你自己。如果你晚点,那不是我的错。不过,这也是为了兼容目的而允许原始类型,而不是擦除。l
ClassCastException