首页 / 软件开发 / JAVA / Java:所有的equals方法实现都是错误的?
Java:所有的equals方法实现都是错误的?2011-01-05司马牵牛译本文介绍了一种改写(override)equals 方法的技巧。使用该技巧,即使在实体类的子类添加了新的域(field)时,仍然能够满足 equals 方法的约定。在《Effective Java》一书的第 8 条目中,Josh Bloch 将子类化时满足 equals 约定这一困难描述为:面向对象语言中等值关系的最根本问题。Bloch 这样写道:不存在一种方式,能够在扩展非实例类并添加值组件的同时,仍然满足equals的约定。除非你愿意放弃面向对象的抽象性这一优点。《Programming in Scala》一书中的第 28 章提供了一种方法,子类可以对非实例类进行扩展,添加值组件,而同时满足 equals 约定。虽然书中提供的那种技巧是用于定义 Scala 类,但一样适用于 Java 中的 类定义。在本文中,为了讲解这种方法,我将使用《Programming in Scala》中相关章节,改编相关的文本,并将原书中的 Scala 示例代码转换为了 Java 代码。常见的等值陷阱Class java.lang.Object 定义了一个 equals 方法,其中的子类可以进行改写(override)。不幸的是,最终的结果表明,在面向对象语言中,编写正确的等值方法相当困难。事实上,在对 Java 代码的大量正文进行研究之后,几位作者在 2007 年的一份论文中作出如下结论:几乎所有 equals 方法的实现都是错误的。这是一个严重的问题,因为等值方法是很多代码的根本。其一,对于类型 C,一个错误的等值方法可能意味着,你不能可靠地将一个类型 C 的对象放入集合中。你可能有两个等值的类型 C 元素 elem1、elem2,即“em1.equals(elem2)”输出 true。然而,在下面的示例中,equals 方法的实现就是一种常见的错误:Set< C> hashSet = new java.util.HashSet< C>();
hashSet.add(elem1);
hashSet.contains(elem2); // 返回 false!存在四种常见的陷阱,它们都会在改写equals时导致非一致性的行为:◆使用错误的原型对equals进行定义。◆更改equals而未同时更改 hashCode。◆对equals进行定义时涉及可变域(field)。◆未能成功地将equals定义为等值关系。这四种陷阱将在下文中具体讲述。陷阱 1:使用错误的原型对equals进行定义在下面的代码中,我们将为普通点的类添加一个等值方法: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;
}
// ...
}