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

时间:2019-06-07 14:46:30

标签: java generics backwards-compatibility java-5 java1.4

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

例如。

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

如果我这样做,

A a = new A();

a.myMethod将要求我将Object作为输入传递,其返回类型为Object。显然,类类型参数'T'与myMethod()特定的泛型'U'不冲突。但是Java却以某种方式说,为了处理向后兼容性,他们删除了原始实例对所有泛型的使用。

上面的问题被其他人多次问,所有答案都只说了一次,因为与早期版本的向后兼容性Java不允许这样做。 [点]。

但是,没有一个答案提供了一个实例,如果Java允许原始实例使用方法特定的泛型,那么在早期版本中可能会失败。

任何人都可以通过提供一个特定的实例来帮助您,在这种情况下,如果允许的话,早于1.5的早期版本中就会出现问题?

请不要提供其他任何与可能导致失败的答案无关的stackoverflow问题。

2 个答案:

答案 0 :(得分:5)

考虑一个Java程序,该Java程序被编写为使用Java 1.5之前的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之前的版本
  • 具有泛型和类型参数的1.5版之后的集合

由于Java类型等效项基于类型名称/标识而不是基于签名的(或鸭子类型)等效项,因此这两个集合层次结构将不兼容。这意味着使用集合的API和实现需要在1.5之前的集合和1.5以后的集合之间进行选择。混合它们会很尴尬……而且效率低下。

对于需要使用旧版Java库和应用程序的人员/组织,最终结果将是一个大问题。基本上,迁移到Java 1.5意味着很多重写工作正常的应用程序。那会杀死泛型,而Java则成为企业语言。


我们无法为您提供具体的示例,这些示例在通用语言是基于模板和/或没有擦除的Java语言中无法证明是有效的。

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

答案 1 :(得分:2)

向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团队在这里做出了更好的选择,因为它允许他们发展语言而不是永久限制语言。至少,十年后无需解释其设计决策的原理:-)