为什么在对象中定义了等于和哈希码?

2022-09-03 01:54:34

决定将这些方法包含在java.lang.Object中的原因是什么?相等和散列对许多类没有意义。

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

interface Equalable {
    boolean equals(Equalable other);
}

interface Hashable extends Equalable {
    int hashCode();
}

例如,哈希集定义可能如下所示

class HashSet<T extends Hashable> ...

它可以防止一个常见的初学者错误 - 使用一组项目而不实现equals/hashCode。


答案 1

当我们实现一个接口时,我们注入(或接受)由接口定义的契约。

Equalable & Hashable是两个不同的合同。但是,如果我们仔细观察,那么我们将看到它们都相互依赖,这意味着它们是 的一部分 ,类似于 。single interfaceEqualableAndHashable

现在显而易见的问题是,它们是否应该成为这个新界面的一部分?EqualableAndHashableObject

让我们来一探究竟。我们有(相等运算符)来检查两个对象的相等性。 运算符确认两个不同的基元/对象的值/引用是否相等。但是,仅通过与操作员核对,并不总是可以回答这个问题。======

现在的问题是,这种平等,也是一个契约,是否应该通过接口或Object类的一部分注入?

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

TypeX不保证平等合同。

如果某些对象类型提供相等性,而某些对象类型不提供相等性,它将变得混乱。这意味着 对象 必须遵守相等协定,这对于所有其他对象类型也是真的。所以,它一定不能从接口中注入平等,因为平等应该是任何对象的合约的一部分,否则会产生混乱。TypeX

因此,我们需要对象来提出 .但它不能只实现方法,还需要实现方法。equalsequalshashcode


答案 2

java.lang.Object 中的默认实现是有意义的。很多时候,这已经足够好了。在JPA /Web应用程序中,我发现自己很少覆盖等于和哈希码。

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

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

不过,这是一个合理的问题,为什么默认方法没有像默认的Object.clone()那样锁定在标记界面后面。有一个默认的实现,但你必须明确承认你想通过实现 Cloneable 来使用它。可以很容易地有一个类似的标记界面,如Collectible或Equalable,然后集合方法的签名可以是Equalable而不是Object。