【问题标题】:C# vs Java generics [duplicate]C# 与 Java 泛型 [重复]
【发布时间】:2010-09-26 04:26:54
【问题描述】:

我听说泛型的 Java 实现不如 C# 实现。在语法看起来很相似的情况下,Java 实现有什么不合标准的地方,还是宗教观点?

【问题讨论】:

标签: c# java generics comparison


【解决方案1】:

streloksi's link 在消除差异方面做得很好。快速而肮脏的总结是......

在语法和用法方面。语言之间的语法大致相同。这里和那里的一些怪癖(最明显的是在约束中)。但基本上,如果你能读懂一个,你就可以读/用另一个。

最大的区别在于实现。

Java 使用类型擦除的概念来实现泛型。简而言之,底层编译的类实际上并不是通用的。它们编译为 Object 和 casts。实际上,Java 泛型是编译时工件,在运行时很容易被破坏。

另一方面,借助 CLR,C# 实现了泛型,一直到字节码。为了在 2.0 中支持泛型,CLR 进行了几项重大更改。好处是性能改进、深度类型安全验证和反射。

再次提供的link 有更深入的细分,我鼓励您阅读

【讨论】:

  • 我已经阅读了您的 Strelok 链接、Anders Hejlsberg 的 interview 和 Eric Lippert 的 blog-post。但我仍然无法理解 - C# 不进行类型擦除 引用类型如何共享相同的 IL 代码?能否请您扩展一下?
  • @AlexanderMalakhov 所有引用类型在汇编中都是一个字长,并且可以使用相同的指令进行操作,因此它们可以共享相同的汇编模式。您希望它们在哪里有所不同?
  • 我猜 Java 做的完全一样。那么,类型信息在哪里?例如,反射如何工作?
  • @AlexanderMalakhov 每个实例化仍然有一个唯一的类型对象,只是方法槽都指向同一个实现。
  • Java 10 没有这样的计划。想知道它的性能有多高!
【解决方案2】:

差异归结为 Microsoft 和 Sun 的设计决定。

Generics in Java是编译器通过type erasure实现的,也就是说在编译时进行类型检查,去掉类型信息。采用这种方法是为了保持旧代码与使用泛型的新代码兼容:

来自 Java 教程,Generics: Type Erasure

当一个泛型类型被实例化时, 编译器通过 一种称为类型擦除的技术—— 编译器删除所有的过程 类型参数相关信息 并在类中键入参数或 方法。类型擦除启用 Java 使用泛型的应用程序 保持二进制兼容性 Java 库和应用程序 是在泛型之前创建的。

但是,使用generics in C# (.NET),编译器不会擦除类型,并且类型检查是在运行时执行的。这样做的好处是类型信息保留在编译的代码中。

来自维基百科:

这种设计选择用于 提供额外的功能,例如 作为允许反射 保存泛型类型,以及 作为减轻一些限制 擦除(例如无法 创建通用数组)。这 也意味着没有 运行时强制转换对性能的影响和 通常昂贵的拳击转换。

与其说“.NET 泛型比 Java 泛型更好”,不如研究一下实现泛型的方法的差异。在 Java 中,保持兼容性似乎是一个高优先级,而在 .NET(在 2.0 版中引入时),实现使用泛型的全部好处是一个更高的优先级。

【讨论】:

  • @Tom “分析豁免”是什么意思?
  • 这意味着,是的,您可以说“.NET 泛型优于 Java 泛型”。 Java 应该采取这种方法做一个重大的改变,并在他们有机会这样做的时候正确地实现泛型。
  • “向后兼容性”不正确,它们都是向后兼容的。这样做是为了“迁移兼容性”。
  • @kervin 希望你能详细说明一下。
【解决方案3】:

还发现this 与 Anders Hejlsberg 的对话也可能很有趣。总结一下 Anders Hejlsberg 的几点补充说明:Java 泛型是为了最大限度地与现有 JVM 兼容,与您在 C# 中看到的实现相比,这几乎没有什么奇怪的事情:

  • 类型擦除强制实现将每个通用参数化值表示为Object。虽然编译器在 Object 和更具体的类型之间提供自动转换,但它不会消除类型转换和装箱对性能的负面影响(例如,Object 被转换为特定类型 MyClassint 必须装在 Integer 中,如果 C#/.NET 由于用户定义的值类型而遵循类型擦除方法,这将更加严重)。正如 Anders 所说:“你没有得到任何执行效率”(在 C# 中实现的具体化泛型)

  • 类型擦除使编译时可用的信息在运行时无法访问。曾经是List<Integer> 的东西变成了List,无法在运行时恢复泛型类型参数。 这使得围绕 Java 泛型构建反射或动态代码生成场景变得困难。 最近的SO answer 展示了一种通过匿名类解决它的方法。但是如果没有技巧,在运行时通过反射生成代码,从一个集合实例中获取元素并将其放入另一个集合实例,在动态生成的代码执行期间可能会在运行时失败:反射无助于捕获List<Double> 中的不匹配与List<Integer> 在这些情况下。

但是 +1 链接到 Jonathan Pryor 的blog post 的答案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-06-12
    • 1970-01-01
    • 1970-01-01
    • 2010-11-15
    • 2019-06-04
    • 2014-01-12
    • 2018-04-27
    • 2015-10-08
    相关资源
    最近更新 更多