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

sal*_*sal 68 java generics collections jvm reification

我已经阅读过Neal Gafter关于这个主题博客,但我仍然不清楚其中的一些观点.

为什么在给定Java的当前状态,JVM和现有集合API的情况下,不可能创建保留类型信息的Collections API的实现?难道这些不能以保留向后兼容性的方式替换未来Java版本中的现有实现吗?

举个例子:

List<T> list = REIList<T>(T.Class);
Run Code Online (Sandbox Code Playgroud)

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 ...
Run Code Online (Sandbox Code Playgroud)

并且这些方法使用Object o和Class klass来获取类型信息.

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

我不明白的是什么?

ghe*_*ton 36

重点是,在编译器中,reified泛型支持保留类型信息,而类型擦除泛型则不支持.AFAIK,首先进行类型擦除的重点是启用向后兼容性(例如,较低版本的JVM仍然可以理解泛型类).

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

  • 它为旧JVM编写的源代码和目标代码可以在没有任何更改的情况下在新的JVM上运行(源代码会出现"enum"作为标识符的问题,但这不是泛型的). (5认同)
  • “较低版本的 JVM 仍然可以理解通用类”&lt;-- 我认为当您尝试在旧版本的 JRE/JVM 中运行使用较新版本的 JDK 编译的程序时,您会得到 UnsupportedClassVersionError。所以事实应该是汤姆霍廷和理查德戈麦斯所说的相反。 (2认同)

Ric*_*mes 18

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

关于类型擦除

请注意,在编译时,编译器具有可用完整的类型的信息,但该信息被故意中断的一般当产生的二进制代码,在被称为一个处理类型擦除.由于兼容性问题,这是以这种方式完成的:语言设计者的意图是提供完整的源代码兼容性和平台版本之间的完全二进制代码兼容性.如果以不同方式实现,则在迁移到较新版本的平台时,必须重新编译旧版应用程序.完成它的方式,保留所有方法签名(源代码兼容性),您不需要重新编译任何东西(二进制兼容性).

关于Java中的reified通用

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

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

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

在文章中,我描述了用户对该技术的反应.概括地说,这是一个不起眼的主体和技术(或者图案,如果你喜欢)看起来无关的大多数Java开发.

示例代码

我上面提到的文章链接到源代码,它实现了这个想法.

  • 这与具体化的泛型无关.是的,*类声明中的泛型*类似于泛型参数中的类型,字段类型,方法签名中的类型,超类的类型,内部类的封闭类的类型(匿名类没有什么特别之处)存储在字节码中,因为使用此类并且只有.class文件的其他类需要知道这些内容以强制执行泛型.但是,对reified与非reified泛型的讨论讨论了*运行时对象*的泛型类型,这与你上面讨论的内容无关. (10认同)
  • 不,您只是检索扩展其超类的泛型类型参数.匿名类创建表达式创建一个类的实例,该类是您提供的类型的子类.`final K k1 = new K <java.lang.Double>(){};`与本地类完全相同`类Foo extends K <java.lang.Double> {}; final K k1 = new Foo();` (10认同)
  • 您也可以同样检索任何类的超类,超接口,字段和方法等的编译时信息. (6认同)
  • 这至少不是问题所在。这就是为什么OP所描述的标准化实现需要更改语言而不仅仅是JVM更改。您的答案都不是。 (2认同)

Har*_*lby 12

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

另一方面,.NET泛型实际上创建了新类型 - List<String>在C#中的类型与a不同List<Int>.在Java中,在幕后,它们是相同的 - a List<Object>.这意味着如果你有一个,你不能在运行时查看它们,看看它们被声明为什么 - 只有它们现在是什么.

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

  • 我将List <*>放在反引号中,以便显示类型. (2认同)