我听说Java实现的泛型不如C#实现得好。虽然语法看起来类似,但是Java实现有哪些不足之处?这是一种宗教观点吗?
我听说Java实现的泛型不如C#实现得好。虽然语法看起来类似,但是Java实现有哪些不足之处?这是一种宗教观点吗?
streloksi的链接做了很好的细分差异。 简单地说...
就语法和用法而言,两种语言的语法基本相同,但还存在一些小问题(尤其是在约束方面)。但基本上,如果你能阅读其中一种,你可能也能阅读/使用另一种。
最大的区别在于实现方式。
Java使用类型擦除的概念来实现泛型。简而言之,底层编译类实际上并不是通用的。它们会编译成Object和转换。实际上,Java泛型是编译时的产物,并且可以很容易地在运行时被破坏。
另一方面,由于CLR的优势,C#将泛型实现到字节码层面。CLR在2.0中进行了多次重大变革以支持泛型。优点是性能提高、深度类型安全验证和反射。
再次提供的链接有更详细的细分,我鼓励你去阅读。
这种差异归结为Microsoft和Sun公司的设计决策。
Java中的泛型是通过编译器的类型擦除实现的,这意味着类型检查发生在编译时,并且类型信息被移除。采用这种方法是为了使遗留代码与使用泛型的新代码兼容:
来自Java教程的泛型:类型擦除:
当一个泛型类型被实例化时, 编译器通过一种称为类型擦除的技术来翻译这些类型——这是一种过程,在这个过程中,编译器删除类或方法中与类型参数和类型参数相关的所有信息。类型擦除使使用泛型的Java应用程序能够与创建于泛型之前的Java库和应用程序保持二进制兼容性。
然而,在C#(.NET)的泛型中,编译器没有类型擦除,类型检查是在运行时执行的。这带来了它的好处,即类型信息被保留在编译后的代码中。
来自维基百科:
这种设计选择被利用来提供附加功能,例如允许反射保留泛型类型,以及缓解一些类型擦除的限制(例如无法创建泛型数组)。这也意味着不存在运行时转换和通常昂贵的装箱转换所带来的性能损失。
与其说“ .NET泛型比Java泛型更好”,我们应该关注实现泛型的方法上的区别。在Java中,保持兼容性似乎是一个很高的优先级,而在.NET中(在2.0版本引入时),实现使用泛型的全部好处是更高的优先级。
还发现了这个与安德斯·海尔斯伯格的对话,可能也很有趣。总结一下安德斯·海尔斯伯格提出的一些观点以及一些额外的说明:Java泛型是为了与现有JVM最大程度的兼容而设计的,这导致了一些与C#中实现相比的奇怪之处:
类型擦除强制实现将每个泛型参数化值都表示为Object
。虽然编译器提供了Object
和更具体类型之间的自动转换,但它不能消除类型转换和装箱对性能的负面影响(例如,将Object
转换为特定类型MyClass
或int
必须被封装在Integer
中,在类型擦除方法中,如果遵循用户定义值类型,则对于C#/.NET来说这将更加严重)。正如安德斯所说:“你不会获得任何执行效率”(这是C#中使泛型可反射的方式)。
类型擦除使得在运行时不可访问在编译时可用的信息。曾经被定义为List<Integer>
的东西变成了一个只有List
的东西,在运行时无法恢复泛型类型参数。 这使得在Java泛型中难以构建反射或动态代码生成场景。更近期的SO答案通过匿名类展示了一种解决方法。但是没有技巧,像通过反射在运行时生成代码,从一个集合实例获取元素并将其放入另一个集合实例的情况可能会在动态生成的代码执行期间在运行时失败:在这些情况下,反射无法帮助捕捉到List<Double>
与List<Integer>
之间的不匹配。
但要点赞答案链接到乔纳森·普莱尔的博客文章。