Java 8默认方法是否会破坏源兼容性?

时间:2015-07-02 15:05:26

标签: java java-8 default-method forward-compatibility

通常情况下,Java源代码已向前兼容。在Java 8之前,据我所知,编译后的类源都已经与以后的JDK / JVM版本向前兼容。 [更新:这是不正确的,请参阅下面的注释' enum'等。]但是,在Java 8中添加了默认方法后,似乎不再是这种情况了。

例如,我一直在使用的库具有java.util.List的实现,其中包含List<V> sort()。此方法返回已排序的列表内容的副本。这个库作为jar文件依赖项部署,在使用JDK 1.8构建的项目中运行良好。

但是,后来我有机会使用JDK 1.8和Java重新编译库本身 我发现库不再编译:{{1​​}} - 具有自己的List方法的实现类现在与Java 8 sort()默认方法冲突。 Java 8 java.util.List.sort()默认方法对列表进行排序(返回sort());我的库的void方法 - 因为它返回一个新的排序列表 - 具有不兼容的签名。

所以我的基本问题是:

  • 由于默认方法,JDK 1.8是否为Java源代码引入了前向不兼容性?

此外:

  • 这是第一次这种前向不相容的变化吗?
  • 在设计和实施默认方法时是否考虑或讨论过这个问题?是否记录在任何地方?
  • (不可否认的是)小的不便是否与优惠折扣?

以下是在1.7和1.7下编译和运行的一些代码的示例 运行在1.8以下 - 但不在1.8下编译:

sort()

以下显示正在编译(或未能)并正在运行的代码。

import java.util.*;

public final class Sort8 {

    public static void main(String[] args) {
        SortableList<String> l = new SortableList<String>(Arrays.asList(args));
        System.out.println("unsorted: "+l);
        SortableList<String> s = l.sort(Collections.reverseOrder());
        System.out.println("sorted  : "+s);
    }

    public static class SortableList<V> extends ArrayList<V> {

        public SortableList() { super(); }
        public SortableList(Collection<? extends V> col) { super(col); }

        public SortableList<V> sort(Comparator<? super V> cmp) {
            SortableList<V> l = new SortableList<V>();
            l.addAll(this);
            Collections.sort(l, cmp);
            return l;
        }

    }

}

5 个答案:

答案 0 :(得分:57)

由于默认方法,JDK 1.8是否为Java源代码引入了前向不兼容性?

超类或接口中的任何新方法都可能破坏兼容性。默认方法使不太可能接口中的更改将破坏兼容性。从某种意义上说,默认方法打开了向接口添加方法的大门,你可以说默认方法可能会导致一些破坏的兼容性。

这是第一次这种前向不兼容的变化吗?

几乎可以肯定,因为自Java 1.0以来我们一直在从标准库中继承类。

在设计和实施默认方法时是否考虑或讨论过这个问题?是否记录在任何地方?

是的,有人考虑过了。请参阅Brian Goetz的2010年8月论文"Interface evolution via “public defender” methods"

  
      
  1. 来源兼容性
  2.         

    此方案可能会在修改库接口以插入与现有类中的方法不兼容的新方法的范围内引入源不兼容性。 (例如,如果一个类有一个浮点值的xyz()方法并实现了Collection,并且我们将一个int值xyz()方法添加到Collection中,那么现有的类将不再编译。)

(不可否认的是)不便不便与优惠相比?

之前,更改界面肯定会破坏兼容性。现在,可能。绝对是&#39;到了&#39;可能&#39;可以看到正面或负面。一方面,它可以向接口添加方法。另一方面,它打开了你所看到的那种不兼容性的大门,不仅仅是类,还有接口。

然而,正如Goetz的论文顶部引用的那样,好处大于不便之处:

  
      
  1. 问题陈述
  2.         

    一旦发布,就不可能在不破坏现有实现的情况下向接口添加方法。自图书馆发布以来的时间越长,这种限制就越有可能给其维护者带来悲痛。

         

    在JDK 7中添加Java语言的闭包会对老化的Collection接口造成额外的压力;闭包最重要的好处之一是它可以开发更强大的库。添加一种语言功能可以实现更好的库,同时不扩展核心库以利用该功能,这将是令人失望的。

答案 1 :(得分:9)

  

由于默认方法,JDK 1.8不会引入Java源代码的正向不兼容性吗?

是的,因为你已经看到了自己。

  

这是第一次这种前向不相容的变化吗?

没有。 Java 5 enum关键字也在破坏,因为在此之前,您可以使用不再在Java 5 +中编译的变量命名

  

在设计和实施默认方法时是否考虑或讨论过这个问题?是否记录在哪里?

Orcale Java 8 source incompatibility description

  

(不可否认的是)小的不便是否与优惠相比?

答案 2 :(得分:3)

我们可以与抽象类绘制并行。抽象类旨在被子类化,以便可以实现抽象方法。抽象类本身包含调用抽象方法的具体方法。抽象类可以通过添加更具体的方法自由发展;而这种做法可能会破坏子类。

因此,您所描述的确切问题甚至在Java8之前就存在了。问题更多地体现在Collection API上,因为野外有很多子类。

虽然默认方法的主要动机是在不破坏子类的情况下向现有Collection API添加一些有用的方法,但是由于担心破坏子类,因此必须对其进行过多的自我控制。只有在绝对必要的情况下才会添加默认方法。这里真正的问题是,为什么List.sort被认为是绝对必要的。我认为这是值得商榷的。

无论为什么默认方法在第一位被引入,它现在是API设计者的一个很好的工具,我们应该像抽象类中的具体方法一样对待它们 - 它们需要在前面仔细设计;必须非常谨慎地介绍新的。

答案 3 :(得分:2)

具有讽刺意味的是,接口中的默认方法被引入以允许使用那些接口而不是的现有库中断,同时在接口中引入大量 new 功能。 (向后兼容。)

可能会出现sort方法之类的冲突。支付额外功能的东西。在你的情况下也需要调查(应该使用新的功能吗?)。

Java前向兼容性中断很少,更多的是它的打字系统,它不断扩大。首先是泛型类型,现在是功能接口的推断类型。从版本到版本以及从编译器到编译器都存在细微差别。

答案 4 :(得分:0)

阅读这个问题,我在想它的解决方案。
默认方法解决了向后兼容性问题,但存在前向兼容性问题。 我认为在这种情况下,我们可以使用特定于应用程序的接口来为我们的类添加一些所需的行为,而不是扩展现有的类。我们可以实现这个特定于应用程序的接口并使用它。