这篇博客承接上一篇博客,我们来讲讲重写equals()
方法时候要满足的性质——传递性
用通俗的话来解释传递性就是说:如果A等于B,然后B等于C,那么我们就可以说A等于C
以下我们来举出一个反面例子来帮助理解一下传递性的体现
首先我们有一个Point
类该类有横纵坐标的属性(x
和y
),并且重写了equals()
方法
package com.blog.effective.note8;
/**
* 〈一个点类〉<br>
*
* @author 未绪
* @time 2017/12/17 11:50
*/
public class Point {
//该点是二位平面上的点
private int x;
private int y;
public Point(int x,int y){
this.x=x;
this.y=y;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Point){
Point p = (Point)obj;
return p.x==this.x&&p.y==this.y;
}
return false;
}
}
以上的代码可以知道,如果有两个Point
对象,那么,只要对应的横纵坐标的数值相等,那么就可以判断这两个点是同一个点。
public static void main(String[] args) {
Point p1=new Point(10,20);
Point p2=new Point(10,20);
System.out.println(p1.equals(p2)); // 输出 true
}
然后这个时候我们需求增加了,我们要给这个点加上一个Color
属性,来说明这个点是什么颜色的。
如果我们不重写相应的equals()
方法,显然在比较相等的时候会忽略掉颜色的信息。
如下代码
package com.blog.effective.note8;
import java.awt.*;
/**
* 〈一个带有颜色的点〉<br>
*
* @author 未绪
* @time 2017/12/17 11:52
*/
public class ColorPoint extends Point {
//给改点新增加一个颜色元素
private Color color;
public ColorPoint(int x, int y, Color color) {
super(x, y);
this.color = color;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ColorPoint) {
ColorPoint cp = (ColorPoint) obj;
return super.equals(obj) && this.color == cp.color; //
}
return false;
}
}
这个时候我们来测试一下我们重写的equals()
方法
Point point=new Point(1,1);
ColorPoint colorPoint=new ColorPoint(1,1,Color.BLUE);
System.out.println(point.equals(colorPoint)); //true
System.out.println(colorPoint.equals(point)); //false
我们看到输出得结果就知道这种做法不满足对称性。 point.equals(colorPoint)
调用的是Point
类的equals()
方法,由于忽略了颜色信息,所以只要坐标相等就会返回 true
colorPoint.equals(point)
调用的是ColorPoint
类的equals()
方法,point
点无颜色信息,所以就会返回false
我们可以尝试在ColorPoint
类的equals()
方法在进行混合比较的时候忽略颜色的信息
@Override
public boolean equals(Object obj) {
if(obj instanceof ColorPoint){
// 非混合比较
ColorPoint cp = (ColorPoint) obj;
return super.equals(obj) && this.color == cp.color;
}
if (obj instanceof Point) {
//混合比较
return obj.equals(this);
}
return false;
}
这样的做法满足了对称性,但是牺牲了传递性,如下测试代码
ColorPoint cp1=new ColorPoint(1,1, Color.BLUE);
Point p=new Point(1,1);
ColorPoint cp2=new ColorPoint(1,1, Color.RED);
System.out.println(cp1.equals(p)); //true
System.out.println(p.equals(cp2)); //true
System.out.println(cp1.equals(cp2)); //false
cp1.equals(p)
和p.equals(cp2)
的的比较都是忽略颜色信息的,但是cp1.equals(cp2)
之间的比较却是要考虑颜色信息。
考虑如何解决这样的一个问题呢?
在equals()
方法中使用getClass()
方法代替instanceof
,将Point
类中的equals()
方法写为
@Override
public boolean equals(Object obj) {
if (obj == null || this.getClass() != obj.getClass()) {
return false;
}
Point p = (Point) obj;
return p.x == this.x && p.y == this.y;
}
这样子的话,就只又对象具有相同的实现的时候,才能使对象相等。但是这样的结果应该是无法接受的。
还有一种不错的建议就是——使用复合
我们不再使ColorPoint
继承Point
,而是在其中加入一个Point
属性。
public class ColorPoint {
//给改点新增加一个颜色元素
private Color color;
private Point point;
public ColorPoint(int x, int y, Color color) {
point=new Point(x,y);
this.color = color;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ColorPoint) {
ColorPoint cp = (ColorPoint) obj;
return cp.point.equals(obj) && this.color == cp.color;
}
return false;
}
}