为什么Collection接口有equals()和hashCode()方法?

9
为什么 Collection 接口有 equals(Object o)hashCode() 方法,考虑到任何实现都会默认继承这些方法(来自Object类)?
4个回答

6
Collection JavaDoc中可以得知:
尽管Collection接口对Object.equals的一般规定没有额外的规定,但是实现Collection接口的程序员需要注意:如果选择重写Object.equals,则必须格外小心。虽然无需这样做,最简单的方法是依赖Object类的实现,但是实现者可能希望在默认的“引用比较”之后实现“值比较”。(List和Set接口规定了这种值比较。)
Object.equals方法的一般约定规定equals必须是对称的(换句话说,仅当b.equals(a)时,a.equals(b)。)List.equals和Set.equals的约定声明,列表只等于其他列表,集合只等于其他集合。因此,一个实现既不是List也不是Set接口的集合类的自定义equals方法必须在将该集合与任何列表或集合进行比较时返回false。(按照相同的逻辑,不可能编写正确实现Set和List接口的类。)
而且,尽管Collection接口对Object.hashCode的一般约定没有额外的规定,但程序员应该注意,任何重写Object.equals方法的类都必须重写Object.hashCode方法,以满足Object.hashCode方法的一般约定。特别是,c1.equals(c2)意味着c1.hashCode()==c2.hashCode()。

3
回答您的具体问题:为什么会有这些方法?只是出于方便考虑,可以包含Java文档以提示实现者应该如何使用这些方法(例如,比较值的相等性而不是引用)。

2
除了其他很好的回答之外,在集合界面中,equals方法在该界面中定义,以使某些决策在等于两个收集实例时工作。根据 JAVA 8文档: 更一般地说,各种集合框架接口的实现可以自由利用底层对象方法的指定行为,无论实现者认为何时适当。
所以,您不会因为任何其他原因而添加来自Object类的方法,而只是为了给java文档更多的明确性。这就是为什么您不会在接口的抽象方法中计算这些方法的原因。此外,在JAVA 8中,出于同样的原因,默认情况下Object类的默认方法是不允许的,将生成编译错误。我相信这样做是为了防止这种混淆。因此,如果尝试创建名为hashCode()的默认方法,则无法编译。
这里是来自Lambda FAQ的关于JAVA 8行为的更详细说明:
一个接口不能为Object类的任何方法提供默认实现。这是方法解析中“类优先”的结果:在超类链上找到的方法始终优先于任何出现在任何超接口中的默认方法。特别是,这意味着无法从接口内部提供equals,hashCode或toString的默认实现。
起初,这似乎很奇怪,因为某些接口实际上在文档中定义了它们的equals行为。List接口就是一个例子。那么,为什么不允许这样做呢?
一种原因是,这会使得理解何时调用默认方法变得更加困难。当前规则很简单:如果一个类实现了一个方法,则该方法总是优先于默认实现。由于所有接口实例都是Object的子类,所有接口实例已经有了equals,hashCode和toString的非默认实现。因此,在接口上的默认版本总是无用的,最好不要编译它。
另一个原因是,在接口中提供这些方法的默认实现可能是误导性的。这些方法对对象状态执行计算,但是接口通常无法访问状态;只有实现类可以访问此状态。因此,类本身应该提供实现,而默认方法不太可能有用。

1

补充以上优秀回答,对于这种情况,拥有“equals”或“hashCode”方法是有意义的:

Collection<Whatever> list1 = getArrayList();
Collection<Whatever> list2 = getAnotherArrayList();

if(list1.equals(list2)){
    // do something
}

在接口中没有equals方法的情况下,我们将被迫使用具体类型,这通常不是一个好习惯:

ArrayList<Whatever> list1 = getArrayList();
ArrayList<Whatever> list2 = getAnotherArrayList();

if(list1.equals(list2)){
    // do something
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接