这是很多程序员都有可能写成的代码。注意在本例中,类ColoredPointed不需要重载hashCode,因为新的ColoredPoint类上的equals定义,严格的重载了Point上equals的定义。hashCode的规范仍然是有效,如果两个着色点(colored point)相等,其坐标必定相等,因此它的hashCode也保证了具有同样的值。
对于ColoredPoint类自身对象的比较是没有问题的,但是如果使用ColoredPoint和Point混合进行比较就要出现问题。
Point p = new Point(1, 2); ColoredPoint cp = new ColoredPoint(1, 2, Color.RED); System.out.println(p.equals(cp)); // 打印真 true System.out.println(cp.equals(p)); // 打印假 false |
“p等价于cp”的比较这个调用的是定义在Point类上的equals方法。这个方法只考虑两个点的坐标。因此比较返回真。在另外一方面,“cp等价于p”的比较这个调用的是定义在ColoredPoint类上的equals方法,返回的结果却是false,这是因为p不是ColoredPoint,所以equals这个定义违背了对称性。
违背对称性对于集合来说将导致不可以预期的后果,例如:
Set<Point> hashSet1 = new java.util.HashSet<Point>(); Set<Point> hashSet2 = new java.util.HashSet<Point>(); |
因此虽然p和cp是等价的,但是contains测试中一个返回成功,另外一个却返回失败。
你如何修改equals的定义,才能使得这个方法满足对称性?本质上说有两种方法,你可以使得这种关系变得更一般化或更严格。更一般化的意思是这一对对象,a和b,被用于进行对比,无论是a比b还是b比a 都返回true,下面是代码:
public class ColoredPoint extends Point { // Problem: equals not transitive private final Color color; public ColoredPoint(int x, int y, Color color) { @Override public boolean equals(Object other) { |
在ColoredPoint中的equals的新定义比老定义中检查了更多的情况:如果对象是一个Point对象而不是ColoredPoint,方法就转变为Point类的equals方法调用。这个所希望达到的效果就是equals的对称性,不管”cp.equals(p)”还是”p.equals(cp)”的结果都是true。然而这种方法,equals的规范还是被破坏了,现在的问题是这个新等价性不满足传递性。考虑下面的一段代码实例,定义了一个点和这个点上上两种不同颜色点:
ColoredPoint redP = new ColoredPoint(1, 2, Color.RED); ColoredPoint blueP = new ColoredPoint(1, 2, Color.BLUE); |
redP等价于p,p等价于blueP
System.out.println(redP.equals(p)); // prints true System.out.println(p.equals(blueP)); // prints true |