第8条:覆盖equals时请遵守通用约定

第8条:覆盖equals时请遵守通用约定

覆盖equals方法看起来非常简单,但是很容易导致错误,并且造成严重后果。

那么,什么时候应该覆盖Object.equals呢?一般情况下,如果对于一个类的两个对象,我们想要关注的是它的值是否相等,而不是是否指向同一个类,就可以这么做。但是如果使用了单例模式。

在覆盖equals方法时,应注意以下的规范,这些规范不是必须的,但是如果违反,当其它的对象去使用这个对象时,返回的结果将是不可预期的:

  1. 自反性:要求对象必须等于,这应该是最为基本的要求。因为大部分的集合(Collection)中的contains方法都借助equals方法来实现,所以如果违反了这一条规定,那么如果将这个对象添加到集合中后,调用contains方法却会告诉你该集合不包含你刚刚添加的实例。

  2. 对称性:对于A与B两个对象,当A.equals(B)为true时,B.equals(A)也为true。有时无意中便会违反这一点:

public final class CaseInsensitiveString{
    private final String s;

    public CaseInsensitiveString(String s){
        if(s == null){
            throw new NullPointerException();
        }
        this.s = s;
    }

    @Override
    public boolean equals(Object o){
        if(o instanceof String){
            return s.equals((String)s);
        }
    }
}

如上面这段代码,就会出现这样的问题:

String s = "example";
CaseInsensitiveString cis = new CaseInsensitiveString("example");

cis.equals(s);  //true
s.equals(cis);  //false
  1. 传递性:对于A,B,C三个对象,当A.equals(B)为true而且B.equals(C)为true时,A.equals(C)也应为true。当涉及到继承关系时也就可能碰到这种问题:
public class Point {
    public int size;

    public Point(int size){
        this.size = size;
    }

    @Override
    public boolean equals(Object o){
        if(!(o instanceof Point)){
            return false;
        }
        Point p = (Point)o;
        return p.size == this.size;
    }
}

public class ColorPoint extends Point {
    private Color color;

    public ColorPoint(int size,Color color){
        super(size);
        this.color = color;
    }

    @Override
    public boolean equals(Object o){
        if(!(o instanceof Point)){
            return false;
        }

        if(!(o instanceof ColorPoint)){
            return o.equals(this);
        }

        return super.equals(o) && ((ColorPoint)o).color == color;
    }
}
ColorPoint p1 = new ColorPoint(1, Color.RED);
Point p2 = new Point(1);
ColorPoint p3 = new ColorPoint(1, Color.BLUE);
p1.equals(p2);  //true
p2.equals(p3);  //true
p1.equals(p3);  //false
  1. 一致性:对于A与B两个对象,如果A.equals(B)为true,那么除非A或者B被修改,否则无论何时调用A.equals(B)都应为true。

  2. 非空性:即所有的对象都必须不等于null,这是为了防止NullPointerException的出现。如果在equals方法中使用了instanceof操作符进行类型检查,那么可以省略掉非空判断,因为例如A instanceof B,如果A为null,那么不论B是什么类型都将返回false。

作者在书中提出了覆盖equals方法时的几点建议,其中值得注意的有:
1. 不要企图让equals方法过于智能:如果在equals方法中执行过于复杂的逻辑,那么很容易陷入各种麻烦之中。

  1. 不要将equals方法中的Object参数替换为其他类型:因为如果这么做了,那实际上并没有覆盖Object.equals()方法,而是重载了这个方法,例如:
public boolean equals(MyClass o){
    .......
}

那么如果传入的参数不是MyClass对象或其子类,那么实际上调用的Object.equals()方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值