传递性
简单来说 x=y ,y=z,x=z
无意识的违反这条规则的情形也不难想象。考虑子类的情形,它将一个新的值组件,添加到了超类中,换句话说,子类增加的信息会影响到equals的比较结果,我们首先以一个简单不可变的二维整型不可变的point类作为开始。
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Point)) {
return false;
}
Point p = (Point) o;
return p.x == x && p.y == y;
}
}
假设你想要扩展这个类,为一个点添加颜色信息
public class ColorPoint extends Point {
private final Color mColor;
public ColorPoint(int x,int y,Color color){
super(x,y);
this.mColor = color;
}
}
equals方法会怎么样呢,如果不重写equals方法,则比较的时候color就会被忽略,假设你编写了一个equals方法如下
@Override
public boolean equals(Object o) {
if(!(o instanceof ColorPoint)){
return false;
}
ColorPoint p = (ColorPoint)o;
return super.equals(o)&& mColor == ((ColorPoint) o).mColor;
}
很明显 这个打破了对称性原则,
Point p = new Point(2,3);
ColorPoint cp = new ColorPoint(2,3, Color.RED);
p.equals(cp);//true
cp.equals(p);//false
然后我们来继续修正这个问题,让ColorPoint.equals进行混合比较时忽略颜色信息
@Override
public boolean equals(Object o) {
if(!(o instanceof Point)){
return false;
}
if(!(o instanceof ColorPoint)){
return o.equals(this);
}
ColorPoint p = (ColorPoint)o;
return super.equals(o)&& mColor == ((ColorPoint) o).mColor;
}
这样对称性问题解决了
ColorPoint p1 = new ColorPoint(2,3, Color.RED);
Point p2 = new Point(2,3);
ColorPoint p3 = new ColorPoint(2,3, Color.RED);
System.out.println(p1.equals(p2));//true
System.out.println(p2.equals(p3));//true
p1.equals(p3)//false
上面代码可以看出,对称性解决了,但是却打破了传递性。
那么到底该怎么解决呢?
事实上,这是面上对象语言中关于等价关系的一个基本问题,我们无法在扩展可实例化的类和增加新的值组件,同时保留equals约定
你可能听说过这个的解决方案,在equals中使用getclass代替instanceof
@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;
}
虽然这样不是太糟糕,但是却违反了里氏替换原则(这里就不举例说明了,具体参考原书)
经过上面分析,可能没有一种令人满意的办法来解决,但是还是有一种权宜之计,,根据16条的建议,复合优先于继承,我们不在让ColorPoint继承Point ,而是在ColorPoint中加入一个私有的Point域
public class ColorPoint {
private final Point mPoint;
private final Color mColor;
public ColorPoint(int x, int y, Color color) {
if(color == null){
throw new NullPointerException();
}
mPoint = new Point(x,y);
this.mColor = color;
}
public Point asPoint(){
return mPoint;
}
@Override
public boolean equals(Object o) {
if(!(o instanceof ColorPoint)){
return false;
}
ColorPoint p = (ColorPoint)o;
return p.mPoint.equals(mPoint) && p.mColor == mColor;
}
}