为什么不从下一个 JVM 中删除类型擦除?

Java在Java 5中引入了泛型的类型擦除,因此它们可以在旧版本的Java上运行。这是对兼容性的权衡。从那以后,我们失去了这种兼容性[1] [2] [3]-字节码可以在更高版本的JVM上运行,但不能在早期版本上运行。这看起来像是最糟糕的选择:我们已经丢失了类型信息,我们仍然无法在旧版本上运行为较新版本的JVM编译的字节码。发生了什么事?

具体来说,我问是否有任何技术原因无法在JVM的下一个版本中删除类型擦除(假设与以前的版本一样,它的字节码无论如何都无法在最新版本上运行)。

[3]:对于那些真正喜欢它的人来说,类型擦除可以以类似于 retrolambda 的方式向后移植。

编辑:我认为对向后与向前兼容性定义的讨论掩盖了这个问题。


答案 1

类型擦除不仅仅是可以打开或关闭的字节码功能。

它会影响整个运行时环境的工作方式。如果希望能够查询泛型类的每个实例的泛型类型,则意味着为泛型类的每个对象实例化创建类似于运行时表示形式的元信息。Class

如果你写你不仅要创建三个对象,你可能会创建三个额外的元对象,以反映类型、、和,如果它们以前不存在的话。new ArrayList<String>(); new ArrayList<Number>(); new ArrayList<Object>()ArrayList<String>ArrayList<Number>ArrayList<Object>

考虑到在典型应用程序中使用了数千种不同的签名,其中大多数从未在需要此类反射可用性的地方使用过(由于缺少此功能,我们可以得出结论,目前,所有这些签名都可以在没有此类反射的情况下工作)。List

当然,这成倍增加,一千种不同的泛型列表类型意味着一千种不同的泛型迭代器类型,一千个分离器和流化身,甚至不算实现的内部类。

它甚至会影响没有对象分配的地方,这些地方当前正在引擎盖下进行类型擦除,例如,或,等,每次调用它们时都会返回相同的实例。如果您坚持要以反射方式检查捕获的泛型类型,这将不再起作用。所以如果你写Collections.emptyList()Function.identity()Comparator.naturalOrder()

List<String> list=Collections.emptyList();
List<Number> list=Collections.emptyList();

您必须收到两个不同的实例,每个实例报告不同的实例或将来的等效项。getClass()


似乎,希望获得这种能力的人对他们的特定方法有狭隘的看法,如果他们能够反思地找出一个特定参数是否真的是两种或三种类型中的一种,那就太好了,但从不考虑携带元信息的权重,这些信息可能成百上千个泛型类的数百或数千个泛型实例化。

这是我们必须问我们得到回报的地方:支持可疑编码风格的能力(这就是由于通过Reflectlease找到的信息而改变代码行为的全部内容)。


到目前为止,答案只解决了删除类型擦除的简单方面,即对实际实例类型进行内省的愿望。实际实例具有可以报告的具体类型。正如用户the8472的评论中提到的,删除类型擦除的要求通常还意味着希望能够通过 强制转换为数组或通过 创建数组,或通过 访问类型变量的类型。(T)new T[]T.class

这将引发真正的噩梦。类型变量与具体实例的实际类型不同。类型变量可以解析为 a,例如 举一个(相当简单)的例子。提供必要的元信息将意味着不仅对象分配变得更加昂贵,每个方法调用都可能带来这些额外的成本,甚至更大,因为我们现在不仅在谈论泛型类与实际类的组合,而且还讨论每个可能的通配符组合,甚至是嵌套的泛型类型。? extends Comparator<? super Number>

请记住,类型参数的实际类型也可以引用其他类型参数,从而将类型检查变成一个非常复杂的过程,您不仅必须对每个类型强制转换重复此过程,如果您允许从中创建数组,则每个存储操作都必须重复它。

除了沉重的性能问题之外,复杂性还引发了另一个问题。如果您查看Stackoverflow的错误跟踪列表或相关问题,您可能会注意到该过程不仅复杂,而且容易出错。目前,的每个次要版本都包含有关泛型类型签名匹配的更改和修复,从而影响将接受或拒绝的内容。我很确定,你不希望内部JVM操作(如类型转换,变量赋值或数组存储)成为这种复杂性的受害者,对每个版本中的合法或不合法有不同的想法,或者由于规则不匹配而突然拒绝编译时接受的内容。javacjavacjavac


答案 2

在某种程度上,擦除将在将来通过valhalla项目删除,以实现价值类型的专用实现。

或者更准确地说,类型擦除实际上意味着泛型缺少类型特化,而valhalla将在基元上引入特化。

具体来说,我问是否有任何技术原因无法在下一个版本的JVM中删除类型擦除。

性能。您不必为泛型类型的所有组合生成专用代码,实例或生成的类不必携带类型标签,多态内联缓存和运行时类型检查(编译器生成的检查)保持简单,我们仍然通过编译时检查获得大部分类型安全性。instanceof

当然,也有很多缺点,但权衡已经完成,以及是什么会激励JVM开发人员改变这种权衡的问题。

而且它也可能是兼容性问题,可能存在执行未经检查的强制转换的代码,通过依赖类型擦除来滥用泛型集合,如果强制执行类型约束,则类型擦除会中断。


推荐