我听说泛型的 Java 实现不如 C # 实现好。由于语法看起来很相似,那么 Java 实现的不合格之处是什么呢,还是一种宗教观点呢?
区别在于微软和 Sun 的设计决策。
Java的类型擦除 由编译器通过 类型删除实现,这意味着在编译时进行类型检查,并删除类型信息。采用这种方法是为了使遗留代码与使用泛型的新代码保持兼容:
来自 Java 教程,泛型: 类型擦除:
实例化泛型类型时, 编译器将这些类型转换为 一种叫做类型擦除的技术 编译器在其中删除所有 与类型参数有关的信息 类中的类型参数或 类型擦除启用 Java 使用泛型来 保持与... 的二进制兼容性 Java 库和应用程序 是在泛型之前创建的。
但是,对于 C # (. NET)中的泛型,编译器不会删除类型,类型检查是在运行时执行的。这样做的好处是可以在编译后的代码中保留类型信息。
来自维基百科:
这种设计选择可用于 提供额外的功能,例如 允许反射 泛型类型的保存 减轻了一些限制 删除(例如无法删除) 创建通用数组) 也意味着没有 运行时强制转换和 通常昂贵的拳击转换。
与其说“ .NET 泛型比 Java 泛型好”,不如看看实现泛型的方法有什么不同。在 Java 中,似乎保持兼容性是一个高优先级,而在。NET (当在版本2.0中引入时) ,实现使用泛型的全部好处是一个更高的优先级。
Streloksi 的 link 在打破分歧方面做得很好。
在语法和用法方面。两种语言之间的语法大致相同。到处都有一些怪癖(最明显的是在约束中)。但基本上,如果你能读懂其中一个,你就可能读懂/使用另一个。
不过,最大的区别在于实现。
Java 使用类型擦除的概念来实现泛型。简而言之,基础编译类实际上并不是泛型的。它们可以编译成 Object 和强制转换。实际上,Java 泛型是一个编译时工件,在运行时很容易被破坏。
另一方面,凭借 CLR,C # 实现了一直到字节代码的泛型。为了在2.0版本中支持泛型,CLR 进行了几次重大更改。其好处是性能改进、深度类型安全验证和反思。
再次提供的 链接有一个更深入的分解,我鼓励你阅读
还发现与安德斯·海尔斯伯格的谈话可能也很有趣。为了总结安德斯·海尔斯伯格中的一些附加注意事项: Java 泛型是为了与现有 JVM 的最大兼容性而制作的导致了一些奇怪的事情,而 C # 中的实现是:
类型擦除强制实现将每个泛型参数化值表示为 Object。编译器提供了 Object和特定类型之间的自动转换,而 不会消除类型强制转换和装箱对性能的负面影响(例如 Object被转换为特定类型 MyClass或 int必须在 Integer中装箱,这对 C #/来说更为严重。NET,如果它们由于使用用户定义的值类型而遵循类型擦除方法)。正如 Anders 所说: “你不会得到任何执行效率”(在 C # 中实现了泛型支持)
Object
MyClass
int
Integer
类型擦除使信息在编译时可用,而在运行时 期间无法访问。以前是 List<Integer>的东西现在只是一个 List,没有办法在运行时恢复泛型类型参数。最近的 回答我展示了一种通过匿名类绕过它的方法。但是如果没有技巧的话,比如在运行时通过反射生成代码,从一个集合实例中获取元素并将其放到另一个集合实例中,这样的事情可能会在运行时在执行动态生成的代码时失败: 在这些情况下,反射无助于捕捉 List<Double>和 List<Integer>中的不匹配。
List<Integer>
List
List<Double>
但是,与乔纳森 · 普赖尔的 博客文章相关的答案是 + 1。