Java 为什么在Object中定义equals和hashCode?
决定在java.lang.Object中包含这些方法的原因是什么?相等和散列对于许多类来说没有意义 制作两个接口更符合逻辑:Java 为什么在Object中定义equals和hashCode?,java,oop,equals,hashcode,Java,Oop,Equals,Hashcode,决定在java.lang.Object中包含这些方法的原因是什么?相等和散列对于许多类来说没有意义 制作两个接口更符合逻辑: interface Equalable { boolean equals(Equalable other); } interface Hashable extends Equalable { int hashCode(); } 例如,HashSet定义可能如下所示 class HashSet<T extends Hashable> ...
interface Equalable {
boolean equals(Equalable other);
}
interface Hashable extends Equalable {
int hashCode();
}
例如,HashSet定义可能如下所示
class HashSet<T extends Hashable> ...
类哈希集。。。
它可以防止一个常见的初学者错误——在不实现equals/hashCode的情况下使用一组项。(就个人而言,如果它们在一个接口中,我会将它们都放在那里,以避免至少一类equals
/hashCode
错误。)
我认为您需要对象中的一个实现,作为一种回退机制,这意味着任何东西都会有一个实现,不管接口与否
我怀疑其中很多是历史性的;今天的Java编程看起来与当时的Java编程有很大不同。这是一个通用的实现。如果需要,应该重写实现。否则,您将有一个合理的默认实现
至少平等是必须的。为基本操作创建接口可能会涉及大量开销。Mhh不确定,但Java 1.0发布时,泛型还不存在。它们是在2004年在Java 5.0中添加的。。因此,您的建议无法在Java 1.0上实现,如果您有一个对象列表,并且调用
contains
方法,那么Java
应该做什么?我认为默认实现(比较引用)是一个不错的决定。这样,您就不必为集合中使用的每个类实现自己的equals
和hashcode
。最初,在Java中没有泛型。这是通过允许任何对象
成为任何集合的成员来解决的,因此任何对象
都需要hashCode
和等于
。现在它已经根深蒂固,无法改变。java.lang.Object中的默认实现是有意义的。很多时候,这已经足够好了。在JPA/web应用程序中,我发现自己很少重写equals和hashCode
更好的问题可能是:对于字符串、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,然后collections方法的签名可以是Equatable而不是Object。当我们实现一个接口时,我们注入(或接受)接口定义的契约
Equalable
和Hashable
是两种不同的合同。但如果我们仔细观察,就会发现它们彼此依赖,这意味着它们是单个接口的一部分,类似于equalableandhable
现在一个明显的问题是,它们是否应该成为这个新的equalableandhable
接口或对象的一部分
让我们看看。我们有==
(相等运算符)来检查两个对象的相等性<代码>=
运算符确认两个不同的原语/对象的值/引用是否相等。但是,仅仅通过使用=
操作符进行检查并不总是能够回答这个问题
现在的问题是,是否应该通过接口或对象类的一部分来注入这个等式,它也是一个契约
如果我们看一看,我们不能只是说:
TypeX
不保证合同的平等性
如果某些对象类型提供相等,而某些对象类型不提供相等,则会变得混乱。这意味着TypeX
的对象必须遵守平等契约,这同样适用于所有其他对象类型。因此,它不能从接口注入平等,因为平等在默认情况下应该是任何对象的契约的一部分,否则它将造成混乱
所以我们需要对象来实现equals
。但是它不能只实现equals
方法,它还需要实现hashcode
方法。实际上,这只是为了方便,这样更好。好吧,想想如果没有.equals方法,要实现对象相等需要什么:
AreEqual(Object obj1,Object obj2) {
if(!obj1 instanceof Equalable) return false;
if(!obj2 instanceof Equalable) return false;
return ((Equalable)(obj1).equals((Equalable)obj2);
}
即使对于哈希代码也是如此。有时引用相等就足够了。如果您使HashSet只接受实现Hashable的对象,那么您必须显式地使类可哈希,即使您只希望引用相等。你已经减少了一个伟大的数据结构的通用性
最好让对象有一个默认值(有时足够了).equals和.hashCode函数,并给新手带来一些问题,而不是让这种语言的频繁用户费力地通过更多的繁文缛节。任何对象,无论其类型如何,都可以明智地回答它是否与任何其他对象等效,即使另一个对象的类型是它从未听说过的类型。如果它从未听说过另一个对象的类型,仅此事实就足以报告它与后一个对象不同。在@Kowser关于“混乱”的回答中添加注释:
equals的隐式逻辑要求它必须满足以下特征:
- 它是自反的:对于任何非空参考值x,x.equals(x)应该返回true
- 它是对称的:对于任何非空的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)才应返回true
- 它是可传递的:对于任何非null ref