了解等于方法 [英] Understanding equals method

查看:95
本文介绍了了解等于方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

J。 Bloch在有效的Java中为equals方法的实现提供了一些规则。它们是:


•反身:对于任何非空参考值x,x.equals(x)必须
返回true。


•对称:对于任何非空参考值x和y,当且仅当y.equals(x)返回true时,
x.equals(y)必须返回true。 / p>

•可传递:对于任何非空参考值x,y,z,如果
x.equals(y)返回true,而y.equals(z)返回true,则
x.equals(z)必须返回true。


•一致:对于任何非空引用
值x和y,多次调用x.equals( y)如果没有修改对象上的等值比较中使用的信息
,则
始终返回true或始终返回false。


•对于任何非空
参考值x,x.equals(null)必须返回false。


但是在书的后面,他提到了所谓的Liskov替换原理: / p>


李斯科夫替代原理说,
a的任何重要属性类型也应保留其子类型,以便为该类型写
的任何方法在其子类型上均应同样有效


了解它与等于合约的关系。


问题是关于实现子类的方法吗?


这是书中的示例:

  private static final Set< Point> unitCircle; 

静态{
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);
}

公共类CounterPoint扩展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(); }
}

以及以下实现:

  //损坏-违反了Liskov替换原则(第40页)
@Override public boolean equals(Object o){
if(o == null || o.getClass()! = getClass())
返回false;
点p =(点)o;
return p.x == x&& p.y == y;
}

好吧,违反了,那又怎么办?我不明白。

解决方案

通常用两种方法在equals方法中检查类型:



选项1:instanceof

  if(!( obj instanceof ThisClass)){
返回false;
}

此选项尊重 Liskov替代原则。但是,您不能在子类中添加与equals方法相关的其他属性,而又不会破坏等价关系(自反,对称,可传递)的特征。



选项2:getClass()

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

此选项违反 Liskov替代原则。但是您可以 在子类中添加与equals方法相关的其他属性,而不会破坏等价关系的特征(自反,对称,可传递)。



约书亚·布洛赫(Joshua Bloch)在他的有效Java一书中对此进行了警告。其他属性的默认值:



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



缺点是等于方法变得相当复杂。


  //损坏-违反了Liskov替换原理(第40页)
@Override public boolean equals(Object o){
if(o == null || o.getClass()!= getClass())
返回false;
点p =(点)o;
return p.x == x&& p.y == y;
}

好吧,那又违反了什么?我不明白。


因此,如果您有MyPoint之类的子类(可能会添加其他方法,但不会添加其他属性/字段),然后

  Point p1 = new Point(x,y); 
点p2 =新的MyPoint(x,y);

p1.equals(p2)==假

Set< Point> points = new HashSet<>();
points.add(p1);

points.contains(p2)== false;

尽管两个对象确实代表同一点。



如果改为使用选项1(instanceof),则equals方法将返回true。


J. Bloch in his effective Java provides a several rules for the implementation for equals method. Here they are:

• 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.

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

I don't see how it ties to the equals contracts. Should we actually adhere to it while writing the equals implementation?

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:

Option 1: instanceof

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

This option respects the Liskov substitution principle. 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).

Option 2: getClass()

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

This option violates the Liskov substitution principle. 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).

Joshua Bloch warns about this in his book "Effective Java".

Angelika Langer however mentions a way for "mixed-tpye" comparisons, if you can define default values for additional properties:

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 p.x == x && p.y == 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

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.

这篇关于了解等于方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆