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标签保证此点)