【发布时间】:2011-02-24 00:41:00
【问题描述】:
考虑以下 sn-p:
import java.util.*;
public class EqualsOverload {
public static void main(String[] args) {
class Thing {
final int x;
Thing(int x) { this.x = x; }
public int hashCode() { return x; }
public boolean equals(Thing other) { return this.x == other.x; }
}
List<Thing> myThings = Arrays.asList(new Thing(42));
System.out.println(myThings.contains(new Thing(42))); // prints "false"
}
}
注意contains 返回false!!!我们好像丢了东西!!
错误当然是我们不小心重载,而不是覆盖,Object.equals(Object)。如果我们将class Thing 改为如下所示,那么contains 会按预期返回true。
class Thing {
final int x;
Thing(int x) { this.x = x; }
public int hashCode() { return x; }
@Override public boolean equals(Object o) {
return (o instanceof Thing) && (this.x == ((Thing) o).x);
}
}
Effective Java 2nd Edition,Item 36: Consistently use the Override annotation,使用基本相同的论点来建议应该一致使用@Override。当然,这个建议很好,因为如果我们尝试在第一个 sn-p 中声明 @Override equals(Thing other),我们友好的小编译器会立即指出我们愚蠢的小错误,因为它是重载,而不是覆盖。
然而,本书没有具体介绍的是,重载equals 是否是一个好主意。基本上有3种情况:
- 仅重载,无重载 -- 几乎肯定是错误的!
- 这基本上是上面的第一个 sn-p
- 仅覆盖(无过载)- 一种修复方法
- 这实际上是上面的第二个 sn-p
- 重载和覆盖组合 -- 另一种修复方法
第三种情况用下面的sn-p来说明:
class Thing {
final int x;
Thing(int x) { this.x = x; }
public int hashCode() { return x; }
public boolean equals(Thing other) { return this.x == other.x; }
@Override public boolean equals(Object o) {
return (o instanceof Thing) && (this.equals((Thing) o));
}
}
这里,虽然我们现在有 2 个equals 方法,但仍然有一个相等逻辑,它位于重载中。 @Override 只是委托给重载。
所以问题是:
- “仅覆盖”与“重载和覆盖组合”的优缺点是什么?
- 是否有理由重载
equals,或者这几乎可以肯定是一种不好的做法?
【问题讨论】:
-
我想说最好的做法是使用 eclipse 或类似的 IDE 来生成 equals 方法,只在必要时对生成的代码进行一些编辑。
-
我认为最好的方法是(Eclipse 插件)Project Lombok 的 @EqualsAndHashCode 注释,默认情况下使用您的属性自动生成等号和哈希码(但可配置)。见projectlombok.org
-
@Tim Bender:eclipse 生成的代码简直太糟糕了。您至少必须使用 Guava 的
Objects.equals之类的东西才能获得一些可读性。 -
@maaartinus,我不明白你的建议。 Guava 的 Objects.equal 函数仅仅依赖于有问题的类来正确实现 equals(Object) 方法。作为参考,请阅读无序列表后文档的最后一行:docs.guava-libraries.googlecode.com/git/javadoc/com/google/…
-
@Tim Bender:比较 this generated code 和 this one。通过使用单行代码
equal,您可以消除大量空测试并使其全部可读。
标签: java equals overloading overriding