什么是具体化泛型?它们如何解决类型擦除问题以及为什么不能在不进行重大更改的情况下添加它们?

问题描述 投票:0回答:5

我已经阅读了 Neal Gafter 的关于该主题的博客,但仍然不清楚很多要点。

鉴于 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 实现更改?

我不明白什么?

java generics collections jvm reification
5个回答
48
投票

重点是具体化泛型在编译器中支持保留类型信息,而类型擦除泛型则不支持。 AFAIK,类型擦除的首要目的是实现向后兼容性(例如,较低版本的 JVM 仍然可以理解泛型类)。

您可以在实现中显式添加类型信息,如上面所示,但是每次使用列表时都需要额外的代码,在我看来这非常混乱。另外,在这种情况下,除非您自己添加检查,否则您仍然无法对所有列表方法进行运行时类型检查,但是具体化泛型将确保运行时类型。


24
投票

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

关于类型擦除

请注意,在编译时,编译器具有可用的完整类型信息,但在生成二进制代码时,通常会故意删除此信息,这一过程称为“类型擦除”。由于兼容性问题,这样做是这样的:语言设计者的目的是在平台版本之间提供完整的源代码兼容性和完整的二进制代码兼容性。如果实施方式不同,则在迁移到较新版本的平台时,您将必须重新编译旧版应用程序。这样做的方式是,所有方法签名都被保留(源代码兼容性),并且您不需要重新编译任何内容(二进制兼容性)。 关于Java中的具体化泛型 如果需要保留编译时类型信息,则需要使用匿名类。 要点是:在匿名类的特殊情况下,可以在运行时检索完整的编译时类型信息,换句话说,这意味着:具体化泛型。

我写了一篇关于这个主题的文章:

https://web.archive.org/web/20151007040203/http://rgomes-info.blogspot.com/2013/12/using-typetokens-to-retrieve-generic.html

在本文中,我描述了我们的用户对该技术的反应。简而言之,这是一个晦涩的主题,并且该技术(或者模式,如果您愿意的话)对于大多数 Java 开发人员来说看起来是无关紧要的。 示例代码

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

IIRC(基于链接),Java 泛型只是使用对象集合和来回转换的现有技术的语法糖。使用 Java 泛型更安全、更简单,因为编译器可以为您进行检查,以验证您是否维护了编译时类型安全性。然而,运行时间是一个完全不同的问题。

另一方面,.NET 泛型实际上创建了新类型 - C# 中的

17
投票

List<Int> 是不同的类型。在 Java 中,它们本质上是同一个东西 - List<Object>

。这意味着,如果您拥有其中的一个,则无法在运行时查看它们并查看它们被声明的内容 - 只能看到它们现在的样子。

具体化泛型将改变这一点,为 Java 开发人员提供与 .NET 中现有的相同功能。 

我不是这个主题的专家,但据我了解,类型信息在编译时会丢失。与 C++ 不同,Java 不使用模板系统,类型安全完全通过编译器实现。在运行时,列表实际上始终是列表。

所以我的看法是,由于 JVM 无法使用类型信息

因为它不存在

,因此需要更改语言规范。

4
投票

我能想到的简单解释 - 他们故意选择简单性而不是代码膨胀,因为代码编译后会被擦除,泛型类及其子类型只有一种实现。

另一方面,C++ 为每个实现创建单独的代码版本。


0
投票
© www.soinside.com 2019 - 2024. All rights reserved.