[英]Understanding equals method
J. Bloch在他的有效Java中为equals方法的实现提供了几条规则。 他们来了:
•Reflexive:对于任何非空引用值x,x.equals(x)必须返回true。
•对称:对于任何非空引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。
•Transitive:对于任何非空引用值x,y,z,如果x.equals(y)返回true而y.equals(z)返回true,则x.equals(z)必须返回true。
•一致:对于任何非空引用值x和y,x.equals(y)的多次调用始终返回true或始终返回false,前提是不修改在对象上的equals比较中使用的信息。
•对于任何非空引用值x,x.equals(null)必须返回false。
但后来他在书中提到了所谓的利斯科夫替代原则:
Liskov替换原则表明类型的任何重要属性也应该适用于其子类型,因此为该类型编写的任何方法都应该在其子类型上同样有效
我不知道它与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方法中的类型:
选项1:instanceof
if (! (obj instanceof ThisClass)){
return false;
}
该选项尊重 Liskov替代原则 。 但是你不能在子类这是相关的而不会断裂的等价关系(自反,对称,传递)的特性equals方法添加附加属性。
选项2:getClass()
if (obj == null || ! this.getClass().equals(obj.getClass())) {
return false;
}
该选项违反了Liskov替换原则 。 但是,您可以在子类中添加与equals方法相关的其他属性,而不会破坏等价关系的特征(自反,对称,传递)。
Joshua Bloch在他的“Effective Java”一书中警告过这一点。
然而,Angelika Langer提到了一种“混合tpye”比较的方法,如果你可以为其他属性定义默认值:
http://www.angelikalanger.com/Articles/JavaSolutions/SecretsOfEquals/Equals-2.html
缺点是等于方法变得相当复杂。
// 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 px == x && py == y; }
好的,违反了什么呢? 我不明白。
因此,如果您有一个子类,如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。
我认为他试图说一个点的特征是它的坐标。 所以你会期望这是真的:
new Point(0, 0).equals(new CounterPoint(0, 0));
因为这两个点具有相同的坐标,即使它们没有相同的类型。 但是建议的equals方法将返回false,因为这两个对象具有不同的类。
如果您考虑集合,例如,这是真的:
new LinkedList().equals(new ArrayList());
这两个列表的类型不同,但它们具有相同的内容(在这种情况下它们都是空的),因此被认为是相同的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.