第八条——覆盖equals方法时需遵守的通用约定

时间:2023-03-10 07:33:28
第八条——覆盖equals方法时需遵守的通用约定

1)自反性

对于任何非null的引用值x,x.equals(x)必须返回true。---这一点基本上不会有啥问题

2)对称性

对于任何非null的引用值x和y,当且仅当x.equals(y)为true时,y.equals(x)也为true。

JDK中就有这样的错误。如java.sql.Timestamp对java.util.Date进行了扩展,并且增加了nanoseconds域。Timestamp的equals方法就违反了对称性。

下面我们来分析下这个方法

//Date.java
public boolean equals(Object obj) {
return obj instanceof Date && getTime() == ((Date) obj).getTime();
} //Timestamp.java
public boolean equals(java.lang.Object ts) {
if (ts instanceof Timestamp) {
return this.equals((Timestamp)ts);
} else {
return false;
}
} public boolean equals(Timestamp ts) {
if (super.equals(ts)) {
if (nanos == ts.nanos) {
return true;
} else {
return false;
}
} else {
return false;
}
}

  比如说Date d ; Timestamp t.那么d.equals(t)==true,但是从Timestamp的equals方法可以看到,t.equals(d)==false

3)传递性

对于任何非null的引用值x、y、z。如果x.equals(y)==true,y.equals(z)==true,那么x.equals(z)==true。

这一条主要是针对子类增加了新的属性导致的.

我们可以再一个抽象的类的子类中增加新的属性,而不会违反equals约定。只要不能直接创建超类的实例,上述几种约定的问题就不会出现。

4) 一致性

对于任何非null的引用值x和y,只要equals的比较操作在对象所用的信息没有被修改,那么多次调用x.eqals(y)就会一致性地返回true,或者一致性的返回false。

5)非空性

所有比较的对象都不能为空。

许多类的equals方法都通过一个显示的null测试来防止这种现象,如下所示:

@override
public boolean equals(Object o) {
if(o==null) {
return false;
}
.........
}

其实没有必要,因为为了测试equals参数的等同性,equals方法必须先把参数转换成适当的类型,以便可以调用它的访问方法,或者域。在进行转换前,equals方法必须使用instanceof操作符,检查其参数是否为正确的类型,如下所示:

@override
public boolean equals(Object o) {
if(!(o instanceof MyType)) {//如果o==null,这一步就返回false
return false;
}
.........
}

综上所述,得出 以下实现高质量equals方法的诀窍

1. 使用==操作符检查“参数是否为这个对象的引用”。。这只不过是一种性能优化,如果比较操作可能跟昂贵,就值得这么做。

2. 使用instanceof操作符检查“参数是否为正确的类型”

3. 把参数转换成正确的类型

4. 对于该类的每个“关键”域,检查参数中的域是否与该对象中对应的域匹配。

注意域的比较顺序可能会影响equals方法的性能,为了获得最佳的性能,应该最先比较最不可能一致的域或者开销最小的域。

5. 当完成equals方法时,一定要检查是否符合:对称性、传递性、一致性

6. 覆盖equals方式时总是要覆盖hashCode方法

7. 不要企图让equals方法过于智能

8.不要把equals方法声明中的参数Object类型替换为其他类型(用@override标签保证此点)