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

2022-08-31 12:25:06

我已经阅读了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 实现更改?

我不明白什么?


答案 1

关键是,已初始化的泛型在编译器中支持保留类型信息,而类型擦除的泛型则不支持。AFAIK,首先进行类型擦除的全部意义在于实现向后兼容性(例如,版本较低的JVM仍然可以理解泛型类)。

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


答案 2

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

关于类型擦除

请注意,在编译时,编译器具有可用的完整类型信息,但在生成二进制代码时,此信息通常会在称为类型擦除的过程中有意删除。由于兼容性问题,这是以这种方式完成的:语言设计人员的目的是在平台版本之间提供完整的源代码兼容性和完整的二进制代码兼容性。如果以不同的方式实现,则在迁移到较新版本的平台时,必须重新编译旧版应用程序。按照它完成的方式,所有方法签名都保留(源代码兼容性),您不需要重新编译任何内容(二进制兼容性)。

关于 Java 中的已初始化泛型

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

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

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

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

示例代码

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