78

我已经阅读了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 类来获取类型信息。

为什么保留通用类信息需要更改语言而不仅仅是 JVM 实现更改?

我不明白什么?

4

5 回答 5

43

关键是,编译器支持具体化泛型来保存类型信息,而类型擦除泛型则不支持。AFAIK,首先进行类型擦除的全部目的是为了实现向后兼容性(例如,较低版本的 JVM 仍然可以理解泛型类)。

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

于 2009-05-18T21:19:11.023 回答
21

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

关于类型擦除

请注意,在编译时,编译器具有可用的完整类型信息,但通常在生成二进制代码时故意删除此信息,该过程称为类型擦除。这样做是因为兼容性问题:语言设计者的目的是在平台版本之间提供完整的源代码兼容性和完整的二进制代码兼容性。如果它以不同的方式实现,那么当您迁移到新版本的平台时,您将不得不重新编译您的遗留应用程序。这样做的方式是保留所有方法签名(源代码兼容性),您不需要重新编译任何东西(二进制兼容性)。

关于 Java 中的具体化泛型

如果需要保留编译时类型信息,则需要使用匿名类。关键是:在匿名类的非常特殊的情况下,可以在运行时检索完整的编译时类型信息,换句话说,这意味着:具体化的泛型。

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

http://rgomes-info.blogspot.co.uk/2013/12/using-typetokens-to-retrieve-generic.html

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

示例代码

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

于 2011-01-27T04:08:58.633 回答
15

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

另一方面,.NET 泛型实际上创建了新类型 - List<String>C# 中的 a 与List<Int>. 在 Java 中,在幕后,它们是同一个东西 - 一个List<Object>. 这意味着,如果您各有一个,您就无法在运行时查看它们并查看它们被声明为什么——只能看到它们现在的样子。

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

于 2009-05-18T21:22:32.140 回答
4

我不是这方面的专家,但据我所知,类型信息在编译时会丢失。与 C++ 不同,Java 不使用模板系统,类型安全完全通过编译器实现。在运行时,List 实际上总是一个 List。

所以我的看法是,需要更改语言规范,因为类型信息对 JVM 不可用,因为它不存在

于 2009-05-18T21:23:06.707 回答
0

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

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

于 2021-08-30T12:33:25.760 回答