C# 与 Java 泛型
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/355060/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C# vs Java generics
提问by johnc
I have heard that the Java implementation of Generics is not as good as the C# implementation. In that the syntax looks similar, what is it that is substandard about the Java implementation, or is it a religious point of view?
我听说泛型的 Java 实现不如 C# 实现。在语法看起来相似的情况下,Java 实现有什么不合标准的地方,还是宗教观点?
采纳答案by JaredPar
streloksi's linkdoes a great job of breaking down the differences. The quick and dirty summary though is ...
streloksi 的链接在分解差异方面做得很好。快速而肮脏的总结是......
In terms of syntax and usage. The syntax is roughly the same between the languages. A few quirks here and there (most notably in constraints). But basically if you can read one, you can likely read/use the other.
在语法和用法方面。语言之间的语法大致相同。在这里和那里有一些怪癖(最明显的是在约束中)。但基本上,如果您可以阅读其中一个,则很可能可以阅读/使用另一个。
The biggest difference though is in the implementation.
但最大的区别在于实现。
Java uses the notion of type erasure to implement generics. In short the underlying compiled classes are not actually generic. They compile down to Object and casts. In effect Java generics are a compile time artifact and can easily be subverted at runtime.
Java 使用类型擦除的概念来实现泛型。简而言之,底层编译类实际上并不是通用的。它们编译为 Object 并进行强制转换。实际上,Java 泛型是编译时的产物,很容易在运行时被破坏。
C# on the other hand, by virtue of the CLR, implement generics all they way down to the byte code. The CLR took several breaking changes in order to support generics in 2.0. The benefits are performance improvements, deep type safety verification and reflection.
另一方面,C# 凭借 CLR 实现泛型直到字节码。为了支持 2.0 中的泛型,CLR 进行了几次重大更改。好处是性能改进、深度类型安全验证和反射。
Again the provided linkhas a much more in depth breakdown I encourage you to read
再次提供的链接有更深入的细分,我鼓励您阅读
回答by coobird
The difference comes down to a design decision by Microsoft and Sun.
差异归结为 Microsoft 和 Sun 的设计决策。
Generics in Javais implemented through type erasureby the compiler, which means that the type checking occurs at compile time, and the type information is removed. This approach was taken to keep the legacy code compatible with new code using generics:
Java 中的泛型是由编译器通过类型擦除来实现的,这意味着类型检查发生在编译时,类型信息被删除。采取这种方法是为了使遗留代码与使用泛型的新代码兼容:
From The Java Tutorials, Generics: Type Erasure:
来自 Java 教程,泛型:类型擦除:
When a generic type is instantiated, the compiler translates those types by a technique called type erasure — a process where the compiler removes all information related to type parameters and type arguments within a class or method. Type erasure enables Java applications that use generics to maintain binary compatibility with Java libraries and applications that were created before generics.
当一个泛型类型被实例化时,编译器通过一种称为类型擦除的技术来转换这些类型——编译器删除与类或方法中的类型参数和类型参数相关的所有信息的过程。类型擦除使使用泛型的 Java 应用程序能够保持与 Java 库和在泛型之前创建的应用程序的二进制兼容性。
However, with generics in C# (.NET), there is no type erasure by the compiler, and the type checks are performed during runtime. This has its benefits that the type information is preserved in the compiled code.
但是,对于C# (.NET) 中的泛型,编译器不会进行类型擦除,并且在运行时执行类型检查。这样做的好处是类型信息保留在编译后的代码中。
From Wikipedia:
来自维基百科:
This design choice is leveraged to provide additional functionality, such as allowing reflection with preservation of generic types, as well as alleviating some of the limitations of erasure (such as being unable to create generic arrays). This also means that there is no performance hit from runtime casts and normally expensive boxing conversions.
这种设计选择用于提供附加功能,例如允许反射并保留泛型类型,以及减轻擦除的一些限制(例如无法创建泛型数组)。这也意味着运行时强制转换和通常昂贵的装箱转换不会影响性能。
Rather than saying ".NET generics is better than Java generics", one should look into the difference in the approach to implement generics. In Java, it appears that preserving compatibility was a high priority, while in .NET (when introduced at version 2.0), the realizing the full benefit of using generics was a higher priority.
与其说“.NET 泛型优于 Java 泛型”,不如研究实现泛型的方法的不同。在 Java 中,保持兼容性似乎是一个高优先级,而在 .NET(在 2.0 版中引入时),实现使用泛型的全部好处是一个更高的优先级。
回答by IgorK
Also found thisconversation with Anders Hejlsberg that may be interesting too. To summarize points that Anders Hejlsberg made with some additional notes: Java generics were made for maximum compatibility with existing JVMthat led to few odd things versus implementation you see in C#:
同时还发现这个谈话安德斯·海尔斯伯格可能也很感兴趣。总结 Anders Hejlsberg 提出的一些附加说明的要点:Java 泛型是为了与现有 JVM 的最大兼容性而设计的,与您在 C# 中看到的实现相比,这导致了一些奇怪的事情:
Type erasure forces implementation to represent every generic parametrized value as
Object
. While compiler provides automatic casts betweenObject
and more specific type, it does not remove the negative impact of the type casts and boxing on performance(e.g.Object
is cast to specific typeMyClass
orint
had to be boxed inInteger
, which would be even more serious for C#/.NET if they followed type erasure approach due to user-defined value types). As Anders said: "you don't get any of the execution efficiency" (that reified generics enable in C#)Type erasure makes information available at compile time not accessible during runtime. Something that used to be
List<Integer>
becomes just aList
with no way to recover generic type parameter at runtime. This makes difficult to build reflection or dynamic code-generation scenarios around Java generics.More recent SO answershows a way around it via anonymous classes. But without tricks, something like generating code at runtime via reflection that gets elements from one collection instance and puts it to another collection instance can fail at runtime during execution of dynamically generated code: reflection doesn't help with catching mismatch inList<Double>
versusList<Integer>
in these situations.
类型擦除强制实现将每个通用参数化值表示为
Object
. 虽然编译器提供了Object
更多特定类型之间的自动转换,但它并没有消除类型转换和装箱对性能的负面影响(例如,转换Object
为特定类型MyClass
或int
必须装箱Integer
,这对于 C#/. NET,如果由于用户定义的值类型而遵循类型擦除方法)。正如安德斯所说:“你没有获得任何执行效率”(在 C# 中启用了具体化的泛型)类型擦除使编译时可用的信息在运行时无法访问。过去的东西
List<Integer>
变成了List
无法在运行时恢复泛型类型参数的东西。这使得围绕 Java 泛型构建反射或动态代码生成场景变得困难。最近的SO 答案显示了通过匿名类解决它的方法。但是,如果没有技巧,通过反射的是会从一个集合实例元素,并将其放入到另一个集合实例的动态生成的代码执行期间可以在运行时失败像生成的代码在运行时:反映不利于在追赶错配List<Double>
与List<Integer>
在这些情况下, .
But +1 for the answer linking to Jonathan Pryor's blog post.
但是 +1 链接到 Jonathan Pryor 的博客文章的答案。