Collection
界面的最低完整定义仅包含两种方法:iterator()
和size()
,这些方法在AbstractCollection
中是抽象的。
为什么所有其他方法都没有在Java 8中默认?兼容性应该不是问题,例如,Iterator.remove()
在Java 7之前是抽象的,但是从Java 8开始就是默认的。
当我希望AbstractCollection
实现成为另一个类层次结构的成员时,有时候来自Collection
的子类很不方便。这不是Java中实际需要接口中的默认方法的原因之一吗?
关于Map
,List
和其他基本接口的相同问题,包括Java Collection Framework。
更新
保罗·桑多兹:一般来说,如果有一个令人信服的理由来帮助实现,我们只会将接口上的现有抽象方法转换为非抽象方法,例如Iterator.remove。
这些不是Collection上的新方法,并且AbstractCollection中已经有实现。将这些抽象转换为非抽象方法的优势并不是特别引人注目,因为它最有可能从AbstractCollection继承或提供更有效的实现。
可以将AbstractCollection上的所有非抽象方法移动到Collection。如果我们从一张白纸开始,这是我们可能做的。 (注意,对于AbstractList上的所有非抽象方法都不能这样做。)
http://mail.openjdk.java.net/pipermail/core-libs-dev/2014-February/025118.html
答案 0 :(得分:5)
默认方法的主要目标是实现接口的兼容演进。请参阅State of the Lambda文档的第10部分。这种演变的主要方向之一是促进内部迭代。请参阅State of the Lambda: Libraries Edition的内部与外部迭代部分。为此,有Iterable.forEach
,Collection.removeIf
和List.replaceAll
等新方法。
添加了List.sort
之类的其他方法,因为它允许单独的具体列表实现提供更有效的排序算法,这不能用Collections.sort
完成。
最后,为了方便起见,添加了默认方法,例如Iterator.remove
。多年来,我们和其他许多人都非常恼火地添加了一个方法remove
,只要我们每次实施新的UnsupportedOperationException
时都会抛出Iterator
。默认方法remove
为我们执行此操作。请注意,这个方法实际上并没有删除任何元素。 (怎么会这样?)
为一堆Collection
方法提供默认实现似乎很方便,这些方法是根据iterator
等其他方法编写的。但是,我不认为它非常有用,事实上我不确定某些方法是否可行。
考虑Collection.contains(Object)
方法。可以想象,通过逐步执行每个元素并比较相等性,可以用iterator
来编写默认的实现。对于类似TreeSet
或HashSet
的内容,这将是一个非常糟糕的实现。即使具体的List
实现(例如LinkedList
和ArrayList
)也提供了快速路径实现,它比步进迭代器更有效。具有Collection.contains
的默认实现可能有点方便,但实际上,它并没有增加太多价值。实际上,每个集合都希望覆盖它。
现在考虑equals
。 Collection.equals
的{{3}}提出了一系列微妙的问题。简而言之,Set
只能等于另一个Set
,List
只能等于另一个List
,equals
操作必须是对称的。因此,既不是Collection
也不是List
的{{1}}永远不能等于Set
或List
。
好的,所以我们的Set
默认方法必须预先进行一堆Collection.equals
检查。如果两者都是instanceof
,我们可以委托给Lists
,如果两者都是AbstractList.equals
,我们可以委托给Sets
。现在让我们假设这个对象和另一个对象既不是AbstractSet.equals
也不是Lists
。如果它们是不同的具体实现并且彼此不相等会怎样?我们无法分辨。
除此之外,让我们假设我们将等式定义为具有相同的成员资格。我们唯一能做的就是遍历每个集合。但是我们不能(通常)对迭代次序做出任何假设,因此我们不能同时迭代它们并像对于列表那样成对地比较元素。相反,我们必须将一个集合中的所有元素加载到某种临时集合中。它不能是Sets
,因为我们可能有重复项。然后我们检查另一个Set
的每个元素,以确保其中的每个元素都在第一个元素中,并且第一个元素中没有额外元素。这不是非常困难,但它很昂贵,并且不支持某些语义,如订单敏感度。
我无法想象任何具体的集合子类实际上都想要使用这种算法。
总之,使用默认方法简化集合实现并不是默认方法的设计目标之一。此外,尽管在Collection
上提供默认方法似乎很方便,但它们实际上看起来并不实用。任何合理的Collection
实现都需要覆盖所有方法,以便提供它想要的语义而不会非常低效。