jvm 什么是物化泛型?它们是如何解决类型擦除问题的?为什么它们不能在没有重大改变的情况下被添加?

7tofc5zh  于 2022-11-07  发布在  其他
关注(0)|答案(5)|浏览(95)

我已经阅读了关于这个主题的Neal Gafter's博客,仍然不清楚一些要点。
为什么在Java、JVM和现有集合API的当前状态下,不可能创建保留类型信息的集合API的实现?这些实现是否可以在Java的未来版本中以保留向后兼容性的方式替换现有实现?
例如:

List<T> list = REIList<T>(T.Class);

其中REIList是这样的:

public REIList<T>() implements List {
  private Object o;
  private Class klass;

  public REIList(Object o) {
    this.o = o;
    klass = o.getClass();
  }
... the rest of the list implementation ...

方法使用Object o和Class klass获取类型信息。
为什么保留泛型类信息需要更改语言,而不仅仅是更改JVM实现?
我不明白什么?

8cdiaqws

8cdiaqws1#

关键在于具体化的泛型在编译器中支持保留类型信息,而类型擦除的泛型则不支持。AFAIK,首先,类型擦除的关键在于支持向后兼容(例如,较低版本的JVM仍然可以理解泛型类)。
你可以在实现中显式地添加类型信息,就像上面那样,但是每次使用列表时都需要额外的代码,在我看来这是相当混乱的。而且,在这种情况下,你仍然没有对所有列表方法进行运行时类型检查,除非你自己添加检查,但是具体化泛型将确保运行时类型。

hrysbysz

hrysbysz2#

与大多数Java开发人员的想法相反,保留编译时类型信息并在运行时检索该信息是可能的,尽管是以一种非常受限的方式。换句话说:Java确实以一种非常有限的方式提供了具体化的泛型

关于擦除类型

请注意,在编译时,编译器有完整的类型信息可用,但在生成二进制代码时,* 一般情况下 * 会有意丢弃此信息,这一过程称为 * 类型擦除 *。由于兼容性问题,这样做的原因是:语言设计者的意图是在平台的不同版本之间提供完全的源代码兼容性和完全的二进制代码兼容性。如果它的实现方式不同,当你迁移到新版本的平台时,你必须重新编译你的遗留应用程序。这样做的话,所有的方法签名都被保留了(源代码兼容性),你不需要重新编译任何东西(二进制兼容性)。

关于Java中的具体化泛型

如果需要保留编译时类型信息,就需要使用匿名类。关键是:在匿名类的非常特殊的情况下,可以在运行时检索完整的编译时类型信息,换句话说,这意味着:具体化的仿制药。
我写了一篇关于这个主题的文章:
http://rgomes-info.blogspot.co.uk/2013/12/using-typetokens-to-retrieve-generic.html
在本文中,我描述了我们的用户对这种技术的React。简而言之,这是一个晦涩的主题,这种技术(或者模式,如果你喜欢的话)对大多数Java开发人员来说似乎是无关紧要的。

示例代码

我上面提到的文章中有一些链接指向实践这个想法的源代码。

8ehkhllq

8ehkhllq3#

IIRC(基于链接),Java泛型只是现有技术中使用Object集合和来回强制转换的语法糖。使用Java泛型更安全、更简单,因为编译器可以为您进行检查,以验证您是否维护了编译时类型安全。然而,运行时是完全不同的问题。
另一方面,.NET泛型实际上创建了新的类型--C#中的List<String>List<Int>是不同的类型。在Java中,它们的本质是一样的--List<Object>。这意味着,如果您有这两种泛型中的一种,您就不能在运行时查看它们,也不能看到它们被声明为什么--只能看到它们现在是什么。
具体化的泛型将改变这种情况,为Java开发人员提供与. NET中现有的功能相同的功能。

qq24tv8q

qq24tv8q4#

我不是这方面的Maven,但据我所知,类型信息在编译时会丢失。与C++不同,Java不使用模板系统,类型安全完全通过编译器实现。在运行时,List实际上总是List。
因此,我认为需要对语言规范进行更改,因为JVM * 无法获得类型信息,因为它不存在 *。

lrpiutwd

lrpiutwd5#

我能想到的简单解释是-他们故意选择简单而不是代码膨胀,因为代码编译后的擦除只存在一个泛型类及其子类型的实现。
另一方面,C++为每个实现创建单独的代码版本。

相关问题