为什么 Java 声称 1.5 版本需要向后兼容?

Why does Java claim 1.5 version need to be backward compatible?

Java 1.5 与泛型一起发布,他们希望它向后兼容早期版本,因此 Java 禁止在创建原始实例时使用特定于方法的泛型。

例如,

class A<T> {
    <U> U myMethod(U u) {
        return u;
    }
}

如果我这样做,

A a = new A();

a.myMethod 会要求我将 Object 作为输入传递,它的 return 类型是 Object。显然 class 类型参数 'T' 与 myMethod() 特定泛型 'U' 不冲突。但是不知何故 java 说,为了处理向后兼容性,他们删除了原始实例的所有泛型的使用。

以上问题被其他人问过多次,所有的答案都围绕着说,因为向后兼容早期版本java不允许。 [点]。

但是 none 的答案提供了一个实例,如果 java 允许对原始实例使用方法特定的泛型,早期版本可能会失败。

任何人都可以提供一个具体实例来提供帮助,如果他们允许的话,问题会在 1.5 之前的早期版本中发生吗?

请不要提供任何其他没有与可能失败的答案相关的 Whosebug 问题。

考虑使用 Java 1.5 之前的 Java 集合类型编写的 Java 程序。

在 Java 1.5 之前,我会写

 List l = new ArrayList();
 l.append("Hello");
 l.append("World");
 String hello = (String) l.get(0);

现在有了类型擦除模型,我可以在 Java 1.5 或更高版本中编译该代码……甚至没有编译器警告。但我也可以以现代方式使用完全相同的集合 类;例如

 List<String> l = new ArrayList<>();
 l.append("Hello");
 l.append("World");
 String hello = l.get(0);

请注意,我在两个示例中使用了相同的 类 和界面。

如果没有擦除模型来掩盖裂缝,Java 设计人员将不得不创建一组并行的 类 和集合接口;即

  • 没有泛型和类型参数的 1.5 之前的集合
  • Post-1.5 具有泛型和类型参数的集合

由于 Java 类型等价是基于类型名称/标识而不是 signature-based(或 duck typing)等价,因此这两个集合层次结构将不兼容。这意味着使用集合的 API 和实现需要在 pre-1.5 和 post-1.5 集合之间选择。混合它们会很尴尬......而且效率低下。

对于需要使用遗留 Java 库和应用程序的人员/组织来说,最终结果将是一个大问题。基本上,迁移到 Java 1.5 意味着对运行良好的应用程序进行大量重写。那会扼杀泛型,Java 作为一种企业语言。


我们不能给你具体的例子,这些例子可以证明在泛型基于模板和/或没有擦除的 Java 语言中不起作用。

  • 假设的 Java 语言不存在。
  • 只要开发人员付出足够的努力,任何 1.5 之前的 Java 程序都可以移植到这种语言。

将泛型添加到 Java 语言时,Java 团队希望满足以下要求(以及其他要求):

  • 为Java 4编译的代码和为Java 5编译的代码必须能够共存于同一个JVM中,并无缝交互
  • 为 Java 4 编写的库代码必须能够以不破坏二进制兼容性的方式移植到 Java 5

这些要求共同确保现有的 Java 代码可以增量和独立地移植到新语言版本,确保其广泛采用,同时最大限度地减少对 Java 生态系统的破坏。

主要困难来自于以不破坏现有代码的方式使用泛型改造 Java API 的集合框架。例如,考虑一个 Java 4 库声明:

public List getFactories();

并在现有的 Java 4 应用程序中使用:

List list = getFactories();

现在假设应用程序更新到 Java 5 之前库。他们指定什么类型的参数?他们可以说:

  • List factories = getFactories();

    ...但这拒绝了新代码使用泛型的机会

  • List<Object> factories = getFactories()

    ...但这并没有消除应用程序代码中不必要的强制转换——如果这些仍然存在,泛型有什么用呢?此外,一旦库确实指定了类型参数,应用程序代码将不得不第二次更新以匹配它。

  • List<Factory> factories = getFactories();

    ... 但这不是类型安全的:由于在创建 List 对象时未告知运行时类型参数,因此无法验证 List 仅包含工厂(或强制保持这种情况,因为遗留代码继续与 List 交互)

  • List<Factory> factories = new ArrayList<Factory>(getFactories);

    ...这可行,但复制整个列表可能很慢

Java 团队选择了选项 3,并通过要求编译器警告类型安全缺失,并让编译器插入合成强制转换来检查每个单独对象的类型来部分减轻影响使用前的列表,从而以轻微的性能成本保持运行时类型系统的完整性(并且当此类转换失败时开发人员的理智成本很大)。

有趣的是,C# 设计团队在几年前将泛型引入 C# 和 CLR 时面临着基本相同的困境,他们选择打破向后兼容性以具体化泛型类型。因此,他们不得不引入一个全新的集合框架(而不是像 Java 那样简单地改造现有的集合框架)。

回想起来,我认为 .NET 团队在这里做出了更好的选择,因为它允许他们发展语言而不是永久保留其局限性。至少,十年后他们的设计决策的基本原理不需要解释:-)