什么是泛化generics? 他们如何解决types擦除问题,为什么不能添加没有重大变化?
我读过Neal Gafter的博客,关于这个问题我还不清楚。
为什么不能创buildCollections API的实现来保存给定Java的当前状态,JVM和现有集合API的types信息? 在未来的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来获取types信息。
为什么要保留generics类信息需要更改语言,而不仅仅是JVM实现的变化?
我不了解什么?
整个观点是,在编译器中为了保存types信息而具体化的generics支持,而types擦除generics没有。 AFAIK,首先进行types擦除的要点是为了实现向后兼容性(例如,较低版本的JVM仍然可以理解generics类)。
您可以像上面那样在实现中显式添加types信息,但是每次使用该列表时都需要额外的代码,而且在我看来这是相当混乱的。 另外,在这种情况下,除非自己添加检查,否则对于所有列表方法仍然没有运行时types检查,但是泛化generics将确保运行时types。
与大多数Java开发人员的看法相反,尽pipe以非常有限的方式,仍然可以在运行时保留编译时types信息并检索这些信息。 换句话说, Java确实以非常有限的方式提供了泛化的generics 。
关于types删除
请注意,在编译时,编译器具有可用的完整types信息,但是在生成二进制代码时,通常故意丢弃该信息,称为types擦除 。 由于兼容性问题,这样做是这样做的:语言devise者的意图是提供完整的源代码兼容性和完整的二进制代码版本之间的平台兼容性。 如果实现方式不同,则在迁移到较新版本的平台时,必须重新编译旧应用程序。 这样做,所有的方法签名被保留(源代码兼容性),你不需要重新编译任何东西(二进制兼容性)。
关于Java中的泛化generics
如果您需要保留编译时types信息,则需要使用匿名类。 重点是:在匿名类的特殊情况下,可以在运行时检索完整的编译时types信息,换句话说就是:泛化的generics。
我写了一篇关于这个主题的文章:
http://rgomes-info.blogspot.co.uk/2013/12/using-typetokens-to-retrieve-generic.html
在文章中,我描述了我们的用户如何对技术做出反应。 简而言之,这是一个晦涩的主题,而技术(或模式,如果您愿意的话)看起来与大多数Java开发人员无关。
示例代码
我上面提到的文章链接到了实现这个想法的源代码。
IIRC(基于链接),Javagenerics只是使用Object集合和来回投射的现有技术的语法糖。 使用Javagenerics更安全更简单,因为编译器可以执行检查,以validation是否保持编译时types安全。 然而,运行时间是完全不同的问题。
另一方面,.NETgenerics实际上创build了新的types – C#中的List<String>
是与List<Int>
不同的types。 在Java中,它们是相同的东西 – 一个List<Object>
。 这意味着如果你有一个,你就不能在运行时看到它们,看看它们被宣布为什么 – 只有它们现在是什么。
泛化的generics会改变这一点,为Java开发人员提供与.NET相同的function。
我不是这方面的专家,但据我了解,编译时会丢失types信息。 与C ++不同,Java不使用模板系统,types安全是通过编译器完全实现的。 在运行时,一个List实际上是一个List,总是。
所以我认为,语言规范的变化是必需的,因为types信息不可用于JVM, 因为它不在那里 。