Java 8 个默认方法会破坏源兼容性吗?

Do Java 8 default methods break source compatibility?

Java 源代码通常是向前兼容的。直到 Java 8,据我所知,编译的 classes 源代码都与后来的 JDK/JVM 版本向前兼容。 [更新:这是不正确的,请参阅下面关于 'enum' 等的评论。] 然而,在 Java 8 中添加默认方法后,情况似乎不再如此。

例如,我一直在使用的库有一个 java.util.List 的实现,其中包括一个 List<V> sort()。此方法returns 对列表内容的副本进行排序。这个库作为 jar 文件依赖项部署,在使用 JDK 1.8.

构建的项目中运行良好

然而,后来我有机会使用 JDK 1.8 和 我发现该库不再编译:List-使用自己的 sort() 方法实现 class 现在与 Java 8 java.util.List.sort() 默认方法冲突。 Java 8 sort() 默认方法对列表进行排序 (returns void);我的图书馆的 sort() 方法 - 因为它 returns 一个新的排序列表 - 具有不兼容的签名。

所以我的基本问题是:

还有:

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

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;
        }

    }

}

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

> c:\tools\jdk1.7.0_10\bin\javac Sort8.java

> c:\tools\jdk1.7.0_10\bin\java Sort8 this is a test
unsorted: [this, is, a, test]
sorted  : [this, test, is, a]

> c:\tools\jdk1.8.0_05\bin\java Sort8 this is a test
unsorted: [this, is, a, test]
sorted  : [this, test, is, a]

> del Sort8*.class

> c:\tools\jdk1.8.0_05\bin\javac Sort8.java
Sort8.java:46: error: sort(Comparator<? super V>) in SortableList cannot implement sort(Comparator<? super E>) in List
                public SortableList<V> sort(Comparator<? super V> cmp) {
                                       ^
  return type SortableList<V> is not compatible with void
  where V,E are type-variables:
    V extends Object declared in class SortableList
    E extends Object declared in interface List
1 error

Doesn't JDK 1.8 introduce a forward incompatibility for Java source code due to default methods?

是的,如你所见。

Is this the first such forward incompatible change?

没有。 Java 5 enum 关键字也被破坏了,因为在此之前你可以有命名的变量,这些变量将不再在 Java 5 +

中编译

Was this considered or discussed when default methods where designed and implemented? Is it documented anywhere?

Orcale Java 8 source incompatibility description

Was the (admittedly small) inconvenience discounted versus the benefits?

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

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

这是第一个这样的向前不兼容的变化吗?

几乎可以肯定不是,因为我们从 Java 1.0 开始就从标准库继承了 类。

在设计和实现默认方法时是否考虑或讨论了这一点?是否在任何地方记录?

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

  1. Source compatibility

It is possible that this scheme could introduce source incompatibilities to the extent that library interfaces are modified to insert new methods that are incompatible with methods in existing classes. (For example, if a class has a float-valued xyz() method and implements Collection, and we add an int-valued xyz() method to Collection, the existing class will no longer compile.)

相对于好处,(公认的小)不便是否被打折了?

以前,更改界面 肯定会 破坏兼容性。现在,它 可能 。从 'definitely' 到 'might' 可以被视为正面或负面。一方面,它使向接口添加方法变得可行。另一方面,它为您看到的那种不兼容打开了大门,不仅与 类,而且与接口也是如此。

不过,正如 Goetz 论文顶部引用的那样,好处多于不便:

  1. Problem statement

Once published, it is impossible to add methods to an interface without breaking existing implementations. The longer the time since a library has been published, the more likely it is that this restriction will cause grief for its maintainers.

The addition of closures to the Java language in JDK 7 place additional stress on the aging Collection interfaces; one of the most significant benefits of closures is that it enables the development of more powerful libraries. It would be disappointing to add a language feature that enables better libraries while at the same time not extending the core libraries to take advantage of that feature.

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

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

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

我们可以和抽象画一个平行线class。抽象 class 旨在被子classed,以便可以实现抽象方法。抽象 class 本身包含调用抽象方法的具体方法。抽象 class 可以通过添加更具体的方法来自由发展;这种做法可能会破坏 subclasses.

因此,您描述的确切问题甚至在 Java8 之前就存在了。这个问题在 Collection APIs 上更加明显,因为有很多 subclasses 在野外。

虽然默认方法的主要动机是在不破坏子classes 的情况下向现有 Collection APIs 添加一些有用的方法,但他们必须很好地控制自己做太多,生怕破坏子classes。仅在绝对必要时才添加默认方法。这里真正的问题是,为什么 List.sort 被认为是绝对必要的。我认为这是值得商榷的。

不管为什么一开始引入默认方法,它现在是 API 设计者的一个很好的工具,我们应该把它和抽象的具体方法一样对待 classes - 他们需要预先仔细设计;必须非常谨慎地引入新的。

看到这个问题,我正在思考它的解决方案。
默认方法已经解决了向后兼容问题,但向前兼容问题仍然存在。
我认为在这种情况下,与其扩展现有的 classes,不如让我们的应用程序特定接口向我们的 class 添加一些所需的行为。我们可以实现这个应用程序特定的接口并使用它。