简体   繁体   English

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

[英]Why not remove type erasure from the next JVM?

Java introduced type erasure with generics in Java 5 so they would work on old versions of Java. Java 在 Java 5 中引入了带有泛型的类型擦除,因此它们可以在旧版本的 Java 上工作。 It was a tradeoff for compatibility.这是兼容性的权衡。 We've since lost that compatibility [1] [2] [3]--bytecode can be run on later versions of the JVM but not earlier ones.我们已经失去了兼容性[1] [2] [3]——字节码可以在更高版本的 JVM 上运行,但不能在早期版本上运行。 This looks like the worse possible choice: we've lost type information and we still can't run bytecode compiled for newer versions of the JVM on older versions.这看起来是更糟糕的选择:我们已经丢失了类型信息,我们仍然无法在旧版本上运行为新版本 JVM 编译的字节码。 What happened?发生了什么?

Specifically I'm asking if there are any technical reasons why type erasure couldn't be removed in the next version of the JVM (assuming, like previous releases, its bytecode won't be able to run on the last version anyway).具体来说,我是问是否有任何技术原因导致无法在下一版本的 JVM 中删除类型擦除(假设,像以前的版本一样,它的字节码无论如何都无法在上一版本上运行)。

[3]: Type erasure could be backported in a manner similar to retrolambda for those who really like it. [3]:对于那些真正喜欢它的人来说,类型擦除可以以类似于retrolambda的方式向后移植。

Edit: I think the discussion of the definition of backwards vs. forwards compatibility is obscuring the question.编辑:我认为对向后与向前兼容性定义的讨论掩盖了这个问题。

Type erasure is more than just a byte code feature that you can turn on or off.类型擦除不仅仅是您可以打开或关闭的字节码功能。

It affects the way the entire runtime environment works.它影响整个运行时环境的工作方式。 If you want to be able to query the generic type of every instance of a generic class, it implies that meta information, comparable to a runtime Class representation, is created for each object instantiation of a generic class.如果您希望能够查询泛型类的每个实例的泛型类型,这意味着为泛型类的每个对象实例化创建与运行时Class表示相当的元信息。

If you write new ArrayList<String>(); new ArrayList<Number>(); new ArrayList<Object>()如果你写new ArrayList<String>(); new ArrayList<Number>(); new ArrayList<Object>() new ArrayList<String>(); new ArrayList<Number>(); new ArrayList<Object>() new ArrayList<String>(); new ArrayList<Number>(); new ArrayList<Object>() you are not only creating three objects, you are potentially creating three additional meta objects reflecting the types, ArrayList<String> , ArrayList<Number> , and ArrayList<Object> , if they didn't exist before. new ArrayList<String>(); new ArrayList<Number>(); new ArrayList<Object>()您不仅创建了三个对象,还可能创建了三个反映类型的额外元对象, ArrayList<String>ArrayList<Number>ArrayList<Object> ,如果它们之前不存在的话.

Consider that there are thousand of different List signatures in use in a typical application, most of them never used in a place where the availability of such Reflection is required (due to the absence of this feature, we could conclude that currently, all of them work without such a Reflection).考虑到在典型应用程序中使用了数千种不同的List签名,其中大多数从未在需要此类反射的地方使用过(由于缺少此功能,我们可以得出结论,目前,所有这些签名没有这样的反射工作)。

This, of course, multiplies, thousand different generic list types imply thousand different generic iterator types, thousand spliterator and Stream incarnations, not even counting the internal classes of the implementation.当然,这乘以千种不同的泛型列表类型意味着千种不同的泛型迭代器类型、千种拆分器和流化身,甚至不包括实现的内部类。

And it even affects places without an object allocation which are currently exploting the type erasure under the hood, eg Collections.emptyList() , Function.identity() or Comparator.naturalOrder() , etc. return the same instance each time they are invoked.它甚至会影响没有对象分配的地方,这些地方当前正在探索引擎盖下的类型擦除,例如Collections.emptyList()Function.identity()Comparator.naturalOrder()等。每次调用它们时都返回相同的实例. If you insist on having the particalar captured generic type reflectively inspectable, this won't work anymore.如果您坚持让特定捕获的泛型类型可以反射检查,这将不再起作用。 So if you write所以如果你写

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

you would have to receive two distinct instances, each of them reporting a different on getClass() or the future equivalent.您将不得不接收两个不同的实例,每个实例在getClass()或未来的等效项上报告不同。


It seems, people wishing for this ability have a narrow view on their particular method, where it would be great if they could reflectively find out whether one particular parameter is actually one out of two or three types, but never think about the weight of carrying meta information about potentially hundreds or thousands generic instantiations of thousands of generic classes.看来,希望获得这种能力的人对他们的特定方法有一种狭隘的看法,如果他们能够反思地发现一个特定参数实际上是二种或三种类型中的一种,那就太好了,但从不考虑携带的重量有关数千个泛型类的潜在成百上千个泛型实例的元信息。

This is the place where we have to ask what we gain in return: the ability to support a questionable coding style (this is what altering the code's behavior due to information found via Reflection is all about).这就是我们必须询问我们得到什么回报的地方:支持有问题的编码风格的能力(这是由于通过反射找到的信息而改变代码行为的全部内容)。


The answer so far only addressed the easy aspect of removing type erasure, the desire the introspect the type of an actual instance.到目前为止,答案仅解决了删除类型擦除的简单方面,以及内省实际实例类型的愿望。 An actual instance has a concrete type, which could be reported.实际实例具有可以报告的具体类型。 As mentioned in this comment from the user the8472 , the demand for removal of type erasure often also implies the wish for being able to cast to (T) or create an array via new T[] or access the type of a type variable via T.class .正如提到的此评论用户the8472 ,用于去除类型擦除的需求往往也意味着希望用于能够铸造到(T)或创建经由阵列new T[]或接入经由一个类型变量的类型T.class

This would raise the true nightmare.这将引发真正的噩梦。 A type variable is a different beast than the actual type of a concrete instance.类型变量是与具体实例的实际类型不同的野兽。 A type variable could resolve to a, eg ? extends Comparator<? super Number>类型变量可以解析为a,例如? extends Comparator<? super Number> ? extends Comparator<? super Number> ? extends Comparator<? super Number> to name one (rather simple) example. ? extends Comparator<? super Number>举一个(相当简单的)例子。 Providing the necessary meta information would imply that not only object allocation becomes much more expensive, every single method invocation could impose these additional cost, to an even bigger extend as we are now not only talking about the combination of generic classes with actual classes, but also every possible wildcarded combination, even of nested generic types.提供必要的元信息意味着不仅对象分配变得更加昂贵,每个方法调用都可能增加这些额外的成本,甚至更大的扩展,因为我们现在不仅在谈论泛型类与实际类的组合,而且还有所有可能的通配符组合,即使是嵌套的泛型类型。

Keep in mind that the actual type of a type parameter could also refer to other type parameters, turning the type checking into a very complex process, which you not only have to repeat for every type cast, if you allow to create an array out of it, every storage operation has to repeat it.请记住,类型参数的实际类型也可以引用其他类型参数,从而将类型检查变成一个非常复杂的过程,如果允许从它,每个存储操作都必须重复它。

Besides the heavy performance issue, the complexity raises another problem.除了沉重的性能问题外,复杂性还引发了另一个问题。 If you look at the bug tracking list of javac or related questions of Stackoverflow, you may notice that the process is not only complex, but also error prone.如果你查看javac的 bug 跟踪列表或 Stackoverflow 的相关问题,你可能会注意到过程不仅复杂,而且容易出错。 Currently, every minor version of javac contains changes and fixes regarding generic type signature matching, affecting what will be accepted or rejected.目前, javac每个次要版本都包含有关泛型类型签名匹配的更改和修复,影响将被接受或拒绝的内容。 I'm quite sure, you don't want intrinsic JVM operations like type casts, variable assignments or array stores to become victim of this complexity, having a different idea of what is legal or not in every version or suddenly rejecting what javac accepted at compile-time due to mismatching rules.我很确定,您不希望像类型转换、变量赋值或数组存储这样的内在 JVM 操作成为这种复杂性的受害者,对每个版本中什么是合法的或不合法的有不同的想法,或者突然拒绝javac接受的内容由于规则不匹配而导致编译时。

To some extent erasure will be removed in the future with project valhalla to enable specialized implementations for value types.在某种程度上, valhalla 项目将来会删除擦除,以启用值类型的专门实现。

Or to put it more accurately, type erasure really means the absence of type specialization for generics, and valhalla will introduce specialization over primitives.或者更准确地说,类型擦除实际上意味着没有泛型的类型特化,而 valhalla 将引入对原语的特化。

Specifically I'm asking if there are any technical reasons why type erasure couldn't be removed in the next version of the JVM具体来说,我问是否有任何技术原因导致无法在下一版本的 JVM 中删除类型擦除

Performance.性能。 You don't have to generate specialized code for all combinations of generic types, instances or generated classes don't have to carry type tags, polymorphic inline caches and runtime type checks (compiler-generated instanceof checks) stay simple and we still get most of the type-safety through compile-time checks.您不必为泛型类型、实例或生成的类的所有组合生成专门的代码不必携带类型标记、多态内联缓存和运行时类型检查(编译器生成的instanceof检查)保持简单,我们仍然可以得到最多通过编译时检查的类型安全。

Of course there are also plenty of downsides, but the tradeoff has already been made, and the question what would motivate the JVM devs to change that tradeoff.当然也有很多缺点,但已经做出了权衡,问题是什么会激励 JVM 开发人员改变这种权衡。

And it might also be a compatibility thing, there could be code that performs unchecked casts to abuse generic collections by relying on type erasure that would break if the type constraints were enforced.这也可能是一个兼容性问题,可能有代码执行未经检查的强制转换以通过依赖类型擦除来滥用泛型集合,如果强制执行类型约束就会中断。

Your understanding of backwards compatibility is wrong.您对向后兼容性的理解是错误的。

The desired goal is for new JVM's to be able to run old library code correctly and unchanged even with new code.期望的目标是JVM 能够正确运行库代码,即使使用代码也不会改变。 This allows users to upgrade their Java versions reliably even to much newer versions than the code was written for.这允许用户将他们的 Java 版本可靠地升级到比编写代码时更新的版本。

Specifically I'm asking if there are any technical reasons why type erasure couldn't be removed in the next version of the JVM (assuming, like previous releases, its bytecode won't be able to run on the last version anyway).具体来说,我是问是否有任何技术原因导致无法在下一版本的 JVM 中删除类型擦除(假设,像以前的版本一样,它的字节码无论如何都无法在上一版本上运行)。

Because it would likely destroy backwards compatibility.因为它可能会破坏向后兼容性。 (Real backwards compatibility ... not the mythical kind that your original question talked about.) (真正的向后兼容性......不是你最初的问题所谈论的那种神话般的。)

Yes that is a technical reason, because backwards compatibility is one of the most important properties of the Java language / platform.是的,这是一个技术原因,因为向后兼容性是 Java 语言/平台最重要的特性之一。 At least, that is the perspective of Oracle AND Oracle's paying customers.至少,这是甲骨文和甲骨文付费客户的观点。

If type erasure stops working, suddenly hundreds of billions of dollars of investment in Java-based software made by many, many enterprises over the last 20 years is at risk.如果类型擦除停止工作,那么在过去 20 年中,许多企业在基于 Java 的软件上的数千亿美元投资突然面临风险。


Type erasure could be backported in a manner similar to retrolambda for those who really like it.对于那些真正喜欢它的人来说,类型擦除可以以类似于 retrolambda 的方式向后移植。

Maybe.也许。 But someone needs to demonstrate conclusively that it would be feasible.但需要有人最终证明这是可行的。 And even if it was demonstrated to be feasible, the cost of the retrofitting, reworking code, retesting, etc is going to be huge.即使它被证明是可行的,改造、重新编写代码、重新测试等的成本也将是巨大的。


Ask yourself this.问问自己这个。 Why didn't Sun / Oracle remove Thread.stop() ?为什么 Sun/Oracle 没有删除Thread.stop()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM