快速业务通道

如何在Java中避免equals方法的隐藏陷阱 - 编程入门网

作者 佚名技术 来源 NET编程 浏览 发布时间 2012-06-21

如何在Java中避免equals方法的隐藏陷阱

时间:2011-01-04 blogjava 赵锟译

译者注 :你可能会觉得Java很简单,Object的equals实现也会非常简单,但是事实并不是你想象的这样,耐心的读完本文,你会发现你对Java了解的是如此的少。如果这篇文章是一份Java程序员的入职笔试,那么不知道有多少人会掉落到这样的陷阱中。

摘要

本文描述重载equals方法的技术,这种技术即使是具现类的子类增加了字段也能保证equal语义的正确性。

在《Effective Java》的第8项中,Josh Bloch描述了当继承类作为面向对象语言中的等价关系的基础问题,要保证派生类的equal正确性语义所会面对的困难。Bloch这样写到:

除非你忘记了面向对象抽象的好处,否则在当你继承一个新类或在类中增加了一个值组件时你无法同时保证equal的语义依然正确

在《Programming in Scala》中的第28章演示了一种方法,这种方法允许即使继承了新类,增加了新的值组件,equal的语义仍然能得到保证。虽然在这本书中这项技术是在使用Scala类环境中,但是这项技术同样可以应用于Java定义的类中。在本文中的描述来自于Programming in Scala中的文字描述,但是代码被我从scala翻译成了Java

常见的等价方法陷阱

java.lang.Object 类定义了equals这个方法,它的子类可以通过重载来覆盖它。不幸的是,在面向对象中写出正确的equals方法是非常困难的。事实上,在研究了大量的Java代码后,2007 paper的作者得出了如下的一个结论:

几乎所有的equals方法的实现都是错误的!

这个问题是因为等价是和很多其他的事物相关联。例如其中之一,一个的类型C的错误等价方法可能意味着你无法将这个类型C的对象可信赖的放入到容器中。比如说,你有两个元素elem1和elem2他们都是类型C的对象,并且他们是相等,即elem1.equals(elm2)返回ture。但是,只要这个equals方法是错误的实现,那么你就有可能会看见如下的一些行为:

Set hashSet<C> = new java.util.HashSet<C>(); 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)

考虑为下面这个简单类Point增加一个等价性方法:

public class Point { private final int x; private final int y; public Point(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public int getY() { return y; } // ... }

如何在Java中避免equals方法的隐藏陷阱(2)

时间:2011-01-04 blogjava 赵锟译

看上去非常明显,但是按照这种方式来定义equals就是错误的。

// An utterly wrong definition of equals public boolean equals(Point other) { return (this.getX() == other.getX() && this.getY() == other.getY()); }

这个方法有什么问题呢?初看起来,它工作的非常完美:

Point p1 = new Point(1, 2); Point p2 = new Point(1, 2); Point q = new Point(2, 3); System.out.println(p1.equals(p2)); // prints true System.out.println(p1.equals(q)); // prints false

然而,当我们一旦把这个Point类的实例放入到一个容器中问题就出现了:

import java.util.Has

凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!

分享到: 更多

Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved

地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008

电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134

《中华人民共和国增值电信业务经营许可证》闽B2-20100024  ICP备案:闽ICP备05037997号