为什么JCF接口中的许多方法在Java 8中没有默认?

lev*_*tov 3 java collections java-8 default-method

Collection接口的最小完整定义仅包含两个方法:iterator()size(),它们是抽象的AbstractCollection.

为什么所有其他方法都没有在Java 8中默认?兼容性应该不是问题,例如,Iterator.remove()在Java 7之前是抽象的,但是从Java 8开始就是默认的.

AbstractCollection当我希望Collection实现成为另一个类层次结构的成员时,子类化有时会很不方便.这不是Java中实际需要接口中的默认方法的原因之一吗?

大约同样的问题Map,List以及其他基本的接口,包括Java集合框架.

UPDATE

Paul Sandoz:

通常,如果有一个令人信服的理由来帮助实现,我们只将接口上的现有抽象方法转换为非抽象方法,例如Iterator.remove.

这些不是Collection上的新方法,并且AbstractCollection中已经有实现.将这些抽象转换为非抽象方法的优势并不是特别引人注目,因为它最有可能从AbstractCollection继承或提供更有效的实现.

可以将AbstractCollection上的所有非抽象方法移动到Collection.如果我们从一张白纸开始,这是我们可能做的.(注意,对于AbstractList上的所有非抽象方法,都不能这样做.)

http://mail.openjdk.java.net/pipermail/core-libs-dev/2014-February/025118.html

Stu*_*rks 5

默认方法的主要目标是实现接口的兼容演进.参见Lambda文件的第10节.这种演变的主要方向之一是促进内部迭代.请参阅Lambda:Libraries EditionState of External vs External Iteration部分.为此,有新的方法,如,和.Iterable.forEachCollection.removeIfList.replaceAll

添加了其他方法,List.sort因为它允许单独的具体列表实现提供更有效的排序算法,这是无法完成的Collections.sort.

最后,为了方便起见,添加了默认方法,例如Iterator.remove.多年来,我们和其他许多人都非常恼火地添加了一种方法remove,UnsupportedOperationException每次我们实现新的方法时都会抛出Iterator.默认方法remove为我们这样做.请注意,这个方法实际上并没有删除任何元素.(怎么会这样?)

为一堆Collection方法提供默认实现似乎很方便,这些方法是根据其他方法编写的iterator.但是,我认为这不是很有用,事实上我不确定某些方法是否可行.

考虑一下Collection.contains(Object)方法.可以想象,iterator通过逐步执行每个元素并比较相等性,可以编写默认的实现.对于像a TreeSet或a 这样的东西,这将是一个非常糟糕的实现HashSet.即使是具体List实现,比如LinkedListArrayList提供快速路径实现,是不是通过一个迭代器步进更有效.具有默认实现Collection.contains可能有点方便,但实际上,它并没有增加太多价值.实际上,每个集合都希望覆盖它.

现在考虑equals.该规范Collection.equals提出一堆问题微妙.简而言之,a Set只能等于另一个Set,并且a List只能等于另一个List,并且equals操作必须是对称的.因此,Collection既不是a List也不是a Set永远不能等于a List或a Set.

好的,所以我们的Collection.equals默认方法必须预先做一堆instanceof检查.如果两者都Lists可以委托给我们AbstractList.equals,如果两者都Sets可以委托给我们AbstractSet.equals.现在让我们假设这个对象和另一个对象既不是Lists也不是Sets.如果它们是不同的具体实现并且彼此不相等会怎样?我们无法分辨.

除此之外,让我们假设我们将相等定义为具有相同的成员资格.我们唯一能做的就是遍历每个集合.但是我们不能(通常)对迭代次序做出任何假设,因此我们不能同时迭代它们并像对于列表那样成对地比较元素.相反,我们必须将一个集合中的所有元素加载到某种临时集合中.它不可能是Set因为我们可能有重复.然后我们检查另一个元素Collection以确保其中的每个元素都在第一个元素中,并且在第一个元素中没有额外元素.这并不是非常困难,但它很昂贵,并且不支持某些语义,如订单敏感度.

我无法想象任何具体的集合子类实际上都想要使用这种算法.


总之,使用默认方法使集合实现更容易,这不是默认方法的设计目标之一.此外,尽管提供默认方法似乎Collection很方便,但它们实际上看起来并不实用.任何合理的Collection实现都需要覆盖所有方法,以便提供它想要的语义而不会非常低效.

  • 关于`isEmpty()`默认.使用`size()`是一个很差的默认值,因为一些`size()`实现是O(n),或者在少数情况下是不可数的.`isEmpty()`通常应为O(1).我们选择不提供所有可能的默认值,只是那些非常常用或需要的默认值. (2认同)