再覆盖equals方法是,你应该遵循它的通用约定,下面是约定内容:
自反性(reflexive):对于任何非null的引用值x,x.equals(x)必须返回true。
如果违背,当你把该类的实例添加到集合(collection)中,然后该集合的contains会告诉你,没有包含你刚刚添加的实例。
对称性(symmetric):对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。
public final class CaseInsensitiveString {
private final String s;
public CaseInsensitiveString(String s) {
if (s == null)
throw new NullPointerException();
this.s = s;
}
// Broken - violates symmetry!
@Override public boolean equals(Object o) {
if (o instanceof CaseInsensitiveString)
return s.equalsIgnoreCase(
((CaseInsensitiveString) o).s);
if (o instanceof String) // One-way interoperability!
return s.equalsIgnoreCase((String) o);
return false;
}
//This version is correct.
// @Override public boolean equals(Object o) {
// return o instanceof CaseInsensitiveString &&
// ((CaseInsensitiveString) o).s.equalsIgnoreCase(s);
// }
public static void main(String[] args) {
CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
String s = "polish";
System.out.println(cis.equals(s) + " " + s.equals(cis));
}
}
这个例子就违反了这条约定,当我们使用CaseInsensitiveString所重写的equals方法是是忽略大小写的,但反过来string去调用它的equals方法是区分大小写的,所以这就造成比较不对称。正确的做法应该去除与string互操的那段代码。
传递性(transitive):对于任何非null的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也必须返回true。
书上例子太长了,不写了。
一致性(consistent):对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致的返回true,或者一致的返回false。
都不要是equals方法依赖于不可靠的资源。
非空性(Non-nullity):对于任何非null的引用值x,x.equals(null)必须返回false。
高质量equals方法:
1、使用==操作符检查“参数是否为这个对象的引用”。
2、使用instanceof操作符检查“参数是否为正确的类型”。
3、把参数转换成正确的类型。
4、对于该类的每个“关键(significant)”域,检查参数中的域是否与该对象中对应的域相匹配。
这点myeclipse自动生成的并不完全完美,myeclipse生成如下:
else if (!name.equals(other.name))
return false;
但是,这条是要求你改成:
else if (name != other.name && !name.equals(other.name))
return false;
5、当你编写完成了equals方法之后,应该会问自己三个问题:他是否的对称的、传递的、一致的。
(覆盖equals时总要覆盖hashcode;不要企图让equals方法过于智能;不要将equals声明中的Object对象替换为其他的类型。)
尽量不要省略@Override。