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

我已经阅读了 Neal Gafter 的博客上关于这个主题的文章,但仍然有一些不清楚的地方。

考虑到 Java、 JVM 和现有集合 API 的当前状态,为什么不可能创建保留类型信息的 CollectionsAPI 的实现?难道这些不能以保持向后兼容性的方式替换 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 实现更改?

我不明白什么?

39698 次浏览

整个问题在于,具体化的泛型在编译器中支持保留类型信息,而类型擦除的泛型则不支持。AFAIK,类型擦除的最初目的是为了支持向后兼容性(例如,较低版本的 JVM 仍然可以理解泛型类)。

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

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

.NET generics, on the other hand, actually create new types - a List<String> in C# is a different type than a List<Int>. In Java, under the covers, they are the same thing - a List<Object>. This means that if you have one of each, you can't look at them at run time and see what they were declared as - only what they are now.

具体化的泛型将改变这一点,给予 Java 开发人员与现在存在于.NET 中相同的能力。

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

因此,我的看法是,由于 JVM 因为它不存在无法获得类型信息,因此需要对语言规范进行更改。

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

关于类型删除

注意,在编译时,编译器有完整的类型信息可用,但是在生成二进制代码时,在称为 类型删除的进程中有意删除了这些信息。这是由于兼容性问题: 语言设计者的目的是在平台的不同版本之间提供完整的原始码相容性和完整的二进制代码相容性。如果实现方式不同,那么在迁移到平台的更新版本时,必须重新编译遗留应用程序。按照这种方法,所有的方法签名都被保留(原始码相容性) ,你不需要重新编译任何东西(二进制兼容性)。

关于具体化的 Java的类型擦除

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

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

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

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

样本代码

上面我提到的文章中有一些源代码的链接,可以用来练习这个想法。

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

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