译者注 :你可能会觉得Java很复杂,Object的equals完成也会非常复杂,但是理想并不是你想象的这样,耐烦的读完本文,你会发现你对Java理解的是如此的少。如果这篇文章是一份Java顺序员的入职笔试,那么不知道有多少人会掉落到这样的圈套中。原文转自artima/lejava/articles/equality.html 三位作者都是不同范围的大拿,有兴趣的读者可以从下面这个衔接直接去阅读原文。
摘要
本文描述重载equals方法的技术,这种技术即使是具现类的子类增加了字段也能保证equal语义的正确性。
在《Effective Java》的第8项中,Josh Bloch描述了当承袭类作为面向对象言语中的等价关系的根底成绩,要保证派生类的equal正确性语义所会面对的困难。Bloch这样写到:
除非你遗忘了面向对象抽象的好处,否则在当你承袭一个新类或在类中增加了一个值组件时你无法同时保证equal的语义依然正确
在《Programming in Scala》中的第28章演示了一种方法,这种方法允许即使承袭了新类,增加了新的值组件,equal的语义依然能得到保证。虽然在这本书中这项技术是在运用Scala类环境中,但是这项技术异样可以应用于Java定义的类中。在本文中的描述来自于Programming in Scala中的文字描述,但是代码被我从scala翻译成了Java
常见的等价方法圈套
java.lang.ifsong旗舰店Object 类定义了equals这个方法,它的子类可以经过重载来覆盖它。不幸的是,在面向对象中写出正确的equals方法是非常困难的。理想上,在研讨了大量的Java代码后,2007 paper的作者得出了如下的一个结论:
简直一切的equals方法的完成都是错误的!
这个成绩是由于等价是和很多其他的事物相关联。例如其中之一,一个的类型C的错误等价方法可能意味着你无法将这个类型C的对象可信赖的放入到容器中。比如说,你有两个元素elem1和elem2他们都是类型C的对象,并且他们是相等,即elem1.equals(elm2)前往ture。但是,只需这个equals方法是错误的完成,那么你就有可能会看见如下的一些行为:
Set hashSet = new java.util.HashSet();
hashSet.add(elem1);
hashSet.contains(elem2); // returns false!当equals重载时,这里有4个会引发equals行为不一致的常见圈套:
定义了错误的equals方法签名(signature) Defining equals with the wrong signature.
重载了equals的但没有同时重载hashCode的方法。 Changing equals without also changing hashCode.
树立在会变化字域上的equals定义。 Defining equals in terms of mutable fields.
不满足等价关系的equals错误定义 Failing to define equals as an equivalence relation.
在剩下的章节中我们将依次讨论这4中圈套。
圈套1:定义错误equals方法签名(signature)