为了消除这个问题,只需在equals方法中去掉对String的不良互操作即可:——为避免违反对称性,equals()参数仅考虑当前类的情况
[java]
@Override
public boolean equals(Object o) {
return o instanceof CaseInsensitiveString &&
((CaseInsensitiveString) o).s.equalsIgnoreCase(s);
}
3)传递性(Transitive)
同样,不难想象无意违反此条要求的情形。【例】如果子类在父类基础上增加了一个新的值组件(value component),也就是说子类增加了信息会影响equals比较。我们以一个简单的不可变的二维整数point类作为开始。
[java]
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 == null || ! (o instanceof Point)){
return false;
}
Point p = (Point) o;
return p.x == x && p.y == y;
}
}
然后扩展这个类,加入颜色的概念:
[java]
class ColorPoint extends Point{
private final Color color;
public ColorPoint(int x, int y, Color c) {
super(x, y);
this.color = c;
}
}
子类的equals应该如何编写的?如果不编写,则从Point类继承,则equals比较时会忽略颜色信息。虽然这样并不违反equals约定,但是显然是无法接受的。
【尝试1】假定仅当equals的参数是另一个ColorPoint,并且拥有相同的位置和颜色信息时才返回true。
[java]
// 与父类违反对称性!!
@Override
public boolean equals(Object o){
if(!(o instanceof ColorPoint)){
return false;
}
ColorPoint p = (ColorPoint)o;
return super.equals(p) && p.color == color;
}
Q:问题是违反了对称性!
[java]
Point p = new Point(1, 2);
ColorPoint cp = new ColorPoint(1, 2, Color.RED);
p.equals(cp)返回true,而cp.equals(c)则返回false。
【尝试2】为了解决这个问题,你或许会尝试在进行ColorPoint.equals进行混合比较时忽略颜色信息:
[java]
@Override
public boolean equals(Object o){
if(! (o instanceof Point)){
return false;
}
if(!(o instanceof ColorPoint2)){ //与Point实例比较时,不考虑Color
return super.equals(o);
}
ColorPoint2 p = (ColorPoint2)o;
return super.equals(p) && p.color.equals(color);
}
Q:这种方式确实提供了对称性,但是却牺牲了传递性。
[java]
ColorPoint p1 = new ColorPoint(1, 2, Color.RED);
Point p2 = new Point(1, 2);
ColorPoint p3 = new ColorPoint(1, 2, Color.BLUE);
p1.equals(p2)、p2.equals(p3)均返回true,而p1.equals(p3)却返回false!前两个比较忽略了颜色信息,第三个比较却又考虑颜色信息。
【尝试3】
那么究竟该如何解决呢? 事实上这是OO语言中等价关系的一个基本问题。无法在扩展可实例化类的时候,增加一个值组件,同时又保证equals约定。
你可能听说过,在equals方法中使用getCalss来代替instanceof,就可以在扩展可实例化类的时候增加值组价,并同时保证equals约定。例如:
[java]
//问题:违反里氏替换原则
@Override
public boolean equals(Object o){
if(o==null || o.getClass()!= getClass()){//如果o是其子类,则永远范围false
return false;
}
Point p = (Point)o;
return p.x == x && p.y == y;
}
这样做的效果是,只有当两个对象是相同的实现类时,才会进行比较。看起来还不错,但是后果却是不可接受的。
假设我们要编写一个方法,判断某个整数点是否在单位圆上:
[java]
// Initialize UnitCircle to contain all Points on the unit circle
private static final Set unitCircle;
static {
unitCircle = new HashSet();
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);
}
现在假设你扩展了Point但不增加值组件,例如在构造方法中记录一共创建了多少个实例:
[java]
public class CounterPoint ext