J. Bloch 在他的有效 Java 中为 equals 方法的实现提供了几个规则。他们在这里:
• Reflexive: For any non-null reference value x, x.equals(x) must return true.
• Symmetric: For any non-null reference values x and y, x.equals(y) must return true if and only if y.equals(x) returns true.
• Transitive: For any non-null reference values x, y, z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) must return true.
• Consistent: For any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
• For any non-null reference value x, x.equals(null) must return false.
但在书的后面他提到了所谓的 Liskov 替换原则:
The Liskov substitution principle says that any important property of a type should also hold for its subtypes, so that any method written for the type should work equally well on its subtypes
我不明白它与 equals
契约有什么关系。在编写 equals 实现时,我们真的应该遵守它吗?
问题是关于实现子类的方法。这是书中的例子:
private static final Set<Point> unitCircle;
static {
unitCircle = new HashSet<Point>();
unitCircle.add(new Point(1, 0));
unitCircle.add(new Point(0, 1));
unitCircle.add(new Point(-1, 0));
unitCircle.add(new Point(0, -1));
}
public static boolean onUnitCircle(Point p) {
return unitCircle.contains(p);
}
public class CounterPoint extends Point {
private static final AtomicInteger counter = new AtomicInteger();
public CounterPoint(int x, int y) {
super(x, y);
counter.incrementAndGet();
}
public int numberCreated() { return counter.get(); }
}
和以下实现:
// Broken - violates Liskov substitution principle (page 40)
@Override public boolean equals(Object o) {
if (o == null || o.getClass() != getClass())
return false;
Point p = (Point) o;
return p.x == x && p.y == y;
}
好吧,违反了,然后呢?我不明白。
最佳答案
通常有两种方法来检查 equals 方法中的类型:
选项一:instanceof
if (! (obj instanceof ThisClass)){
return false;
}
此选项尊重里氏替换原则。但是您不能在不破坏等价关系(自反、对称、传递)特征的情况下,在与 equals 方法相关的子类中添加其他属性。
选项 2:getClass()
if (obj == null || ! this.getClass().equals(obj.getClass())) {
return false;
}
此选项违反里氏替换原则。但是您可以在子类中添加与 equals 方法相关的附加属性,而不会破坏等价关系(自反、对称、传递)的特征。
Joshua Bloch 在他的“Effective Java”一书中警告了这一点。
但是 Angelika Langer 提到了一种“混合类型”比较的方法,如果您可以为其他属性定义默认值:
http://www.angelikalanger.com/Articles/JavaSolutions/SecretsOfEquals/Equals-2.html
缺点是 equals 方法变得相当复杂。
// Broken - violates Liskov substitution principle (page 40) @Override public boolean equals(Object o) { if (o == null || o.getClass() != getClass()) return false; Point p = (Point) o; return p.x == x && p.y == y; }
Ok, violates and what then? I don't understand.
因此,如果您有一个子类,例如 MyPoint(它可能会添加其他方法但不会添加其他属性/字段),那么
Point p1 = new Point(x, y);
Point p2 = new MyPoint(x, y);
p1.equals(p2) == false
Set<Point> points = new HashSet<>();
points.add(p1);
points.contains(p2) == false;
虽然这两个对象实际上代表了同一个点。
如果您改为使用选项 1 (instanceof),则 equals 方法将返回 true。
关于java - 理解equals方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30998600/