简体   繁体   English

了解equals方法

[英]Understanding equals method

J. Bloch in his effective Java provides a several rules for the implementation for equals method. J. Bloch在他的有效Java中为equals方法的实现提供了几条规则。 Here they are: 他们来了:

• Reflexive: For any non-null reference value x, x.equals(x) must return true. •Reflexive:对于任何非空引用值x,x.equals(x)必须返回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. •对称:对于任何非空引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回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. •Transitive:对于任何非空引用值x,y,z,如果x.equals(y)返回true而y.equals(z)返回true,则x.equals(z)必须返回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. •一致:对于任何非空引用值x和y,x.equals(y)的多次调用始终返回true或始终返回false,前提是不修改在对象上的equals比较中使用的信息。

• For any non-null reference value x, x.equals(null) must return false. •对于任何非空引用值x,x.equals(null)必须返回false。

But later in the book he mentioned so-called Liskov substitution principle: 但后来他在书中提到了所谓的利斯科夫替代原则:

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 Liskov替换原则表明类型的任何重要属性也应该适用于其子类型,因此为该类型编写的任何方法都应该在其子类型上同样有效

I don't see how it ties to the equals contracts. 我不知道它与equals合同的关系。 Should we actually adhere to it while writing the equals implementation? 我们是否应该在编写equals实现时坚持它?

The question is about implementing the method for subclasses. 问题是关于实现子类的方法。 Here is the example from the book: 以下是本书中的示例:

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(); }
}

and the following implementation: 以及以下实施:

// 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. 我不明白。

There are typically 2 ways how to check the type in the equals method: 通常有两种方法可以检查equals方法中的类型:

Option 1: instanceof 选项1:instanceof

if (! (obj instanceof ThisClass)){
    return false;
}

This option respects the Liskov substitution principle . 该选项尊重 Liskov替代原则 But you cannot add additional properties in sub classes which are relevant for the equals method without breaking the characteristics of an equivalence relation (reflexive, symmetric, transitive). 但是你不能在子类这是相关的而不会断裂的等价关系(自反,对称,传递)的特性equals方法添加附加属性。

Option 2: getClass() 选项2:getClass()

if (obj == null || ! this.getClass().equals(obj.getClass())) {
    return false;
}

This option violates the Liskov substitution principle . 该选项违反Liskov替换原则 But you can add additional properties in sub classes which are relevant for the equals method without breaking the characteristics of an equivalence relation (reflexive, symmetric, transitive). 但是,您可以在子类中添加与equals方法相关的其他属性,而不会破坏等价关系的特征(自反,对称,传递)。

Joshua Bloch warns about this in his book "Effective Java". Joshua Bloch在他的“Effective Java”一书中警告过这一点。

Angelika Langer however mentions a way for "mixed-tpye" comparisons, if you can define default values for additional properties: 然而,Angelika Langer提到了一种“混合tpye”比较的方法,如果你可以为其他属性定义默认值:

http://www.angelikalanger.com/Articles/JavaSolutions/SecretsOfEquals/Equals-2.html http://www.angelikalanger.com/Articles/JavaSolutions/SecretsOfEquals/Equals-2.html

The downside is that the equals methods becomes rather complicated. 缺点是等于方法变得相当复杂。

 // 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; } 

Ok, violates and what then? 好的,违反了什么呢? I don't understand. 我不明白。

So if you have a sub class such as MyPoint (which might add additional methods but not additional properties/ fields), then 因此,如果您有一个子类,如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;

although both objects really represent the same point. 虽然两个对象确实代表了同一点。

If you would use option 1 (instanceof) instead, the equals method would return true. 如果您改为使用选项1(instanceof),则equals方法将返回true。

I think he is trying to say that the characteristic of a point is its coordinates. 我认为他试图说一个点的特征是它的坐标。 So you would expect this to be true: 所以你会期望这是真的:

new Point(0, 0).equals(new CounterPoint(0, 0));

because the two points have the same coordinates, even if they don't have the same type. 因为这两个点具有相同的坐标,即使它们没有相同的类型。 But the proposed equals method will return false because the two objects have different classes. 但是建议的equals方法将返回false,因为这两个对象具有不同的类。

If you think of collections for example, this is true: 如果您考虑集合,例如,这是真的:

new LinkedList().equals(new ArrayList());

The two lists don't have the same type but they have the same content (in this case they are both empty) and are therefore considered equal. 这两个列表的类型不同,但它们具有相同的内容(在这种情况下它们都是空的),因此被认为是相同的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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