繁体   English   中英

了解equals方法

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM