决定将这些方法包含在java.lang.Object中的背后原因是什么?平等和哈希对于许多类没有意义。
建立两个接口会更合乎逻辑:
interface Equalable {
boolean equals(Equalable other);
}
interface Hashable extends Equalable {
int hashCode();
}
例如,HashSet定义可能看起来像
class HashSet<T extends Hashable> ...
这将防止一个常见的初学者错误-使用项目集而不实现equals / hashCode。
当我们实现一个接口时,我们注入(或接受)该接口定义的合同。
Equalable
和Hashable
是两个不同的合同。但是,如果我们仔细观察,就会发现它们彼此依赖,这意味着它们是single interface
的一部分,类似于EqualableAndHashable
。
现在很明显的问题是,它们应该是此新EqualableAndHashable
接口的一部分还是Object
的一部分?
让我们找出答案。我们有==
(等于运算符)来检查两个对象的相等性。 ==
运算符确认两个不同图元/对象的值/引用是否相等。但这并不总是可能仅通过使用==
运算符进行检查来回答。
现在的问题是,是否应该通过接口或对象类的一部分来注入相等性(也是契约)?
[如果我们看一眼,我们不能只说像这样的话:
[
TypeX
不保证平等合同。
如果某些对象类型提供相等性而另一些对象不提供相等性,则会造成混乱。这意味着TypeX
的对象必须遵守平等协定,这对于所有其他对象类型也是如此。因此,它绝不能从接口注入相等性,因为缺省情况下,相等性应该是任何对象的合同的一部分,否则它将造成混乱。
因此,我们需要Objects提出equals
的实现。但是它不能仅实现equals
方法,还需要实现hashcode
方法。
任何对象,无论其类型如何,都可以明智地回答它是否等效于任何其他对象,即使另一个对象的类型是它从未听说过的类型。如果从未听说过另一个对象的类型,那么仅凭该事实就足以表明它不等同于另一个对象。
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。
Mhh不确定,但是当Java 1.0发布时,泛型尚不存在。它们是2004年在Java 5.0中添加的。。因此您的提案无法在Java 1.0中实现]
(就个人而言,如果它们在接口中,则将它们都放在其中,以避免至少一类equals
/ hashCode
错误。]
向@Kowser关于“混乱”的答案中添加注释:
最初,在Java中,没有泛型。通过允许任何Object
成为任何集合的成员来解决此问题,因此任何Object
都需要hashCode
和equals
。到目前为止,它已经根深蒂固,无法改变。
如果有对象列表,并且调用contains
方法,Java
应该做什么?我认为默认实现(比较参考)是一个不错的决定。这样,您就不必为集合中使用的每个类实现自己的equals
和hashcode
。
它是一个通用的实现。如果需要,您应该覆盖实现。否则,您将具有合理的默认实现。
真的,这只是为了方便,这样更好。好吧,请考虑一下如果没有.equals方法,将执行对象相等该怎么做: