为什么在对象中定义了equals和hashCode?

问题描述 投票:15回答:10

决定将这些方法包含在java.lang.Object中的背后原因是什么?平等和哈希对于许多类没有意义。

建立两个接口会更合乎逻辑:

interface Equalable {
    boolean equals(Equalable other);
}

interface Hashable extends Equalable {
    int hashCode();
}

例如,HashSet定义可能看起来像

class HashSet<T extends Hashable> ...

这将防止一个常见的初学者错误-使用项目集而不实现equals / hashCode。

java oop equals hashcode
10个回答
14
投票

当我们实现一个接口时,我们注入(或接受)该接口定义的合同。

EqualableHashable是两个不同的合同。但是,如果我们仔细观察,就会发现它们彼此依赖,这意味着它们是single interface的一部分,类似于EqualableAndHashable

现在很明显的问题是,它们应该是此新EqualableAndHashable接口的一部分还是Object的一部分?

让我们找出答案。我们有==(等于运算符)来检查两个对象的相等性。 ==运算符确认两个不同图元/对象的值/引用是否相等。但这并不总是可能仅通过使用==运算符进行检查来回答。

现在的问题是,是否应该通过接口或对象类的一部分来注入相等性(也是契约)?

[如果我们看一眼,我们不能只说像这样的话:

[TypeX不保证平等合同。

如果某些对象类型提供相等性而另一些对象不提供相等性,则会造成混乱。这意味着TypeX的对象必须遵守平等协定,这对于所有其他对象类型也是如此。因此,它绝不能从接口注入相等性,因为缺省情况下,相等性应该是任何对象的合同的一部分,否则它将造成混乱。

因此,我们需要Objects提出equals的实现。但是它不能仅实现equals方法,还需要实现hashcode方法。


0
投票

任何对象,无论其类型如何,都可以明智地回答它是否等效于任何其他对象,即使另一个对象的类型是它从未听说过的类型。如果从未听说过另一个对象的类型,那么仅凭该事实就足以表明它不等同于另一个对象。


2
投票

java.lang.Object中的默认实现很有意义。通常,它足够好。在JPA / Web应用程序中,我很少发现自己覆盖了equals和hashCode。

一个更好的问题可能是:对于不可变的值对象,例如String,Long等,为什么不能像在C#中那样覆盖==运算符来调用equals()?因此,我看到的错误远远多于默认的equals / hashCode所做的错误。例如,>

Long x = obj.getId(); 
Long y = obj2.getId();  
if (x == y) { // oops, probably meant x.equals(y)! }

不过,这是一个公平的问题,为什么默认方法没有被锁定在像默认Object.clone()这样的标记接口后面。有一个默认实现,但是您必须通过实现Cloneable明确确认要使用它。同样容易有一个类似的标记接口,例如Collectible或Equatable,然后collection方法的签名可能是Equatable而不是Object。


1
投票

Mhh不确定,但是当Java 1.0发布时,泛型尚不存在。它们是2004年在Java 5.0中添加的。。因此您的提案无法在Java 1.0中实现]


1
投票

(就个人而言,如果它们在接口中,则将它们都放在其中,以避免至少一类equals / hashCode错误。]


1
投票

向@Kowser关于“混乱”的答案中添加注释:


0
投票

最初,在Java中,没有泛型。通过允许任何Object成为任何集合的成员来解决此问题,因此任何Object都需要hashCodeequals。到目前为止,它已经根深蒂固,无法改变。


0
投票

如果有对象列表,并且调用contains方法,Java应该做什么?我认为默认实现(比较参考)是一个不错的决定。这样,您就不必为集合中使用的每个类实现自己的equalshashcode


0
投票

它是一个通用的实现。如果需要,您应该覆盖实现。否则,您将具有合理的默认实现。


0
投票

真的,这只是为了方便,这样更好。好吧,请考虑一下如果没有.equals方法,将执行对象相等该怎么做:

© www.soinside.com 2019 - 2024. All rights reserved.