在重写equals方法时,必须遵循他的一些通用约定(general contracts):
自反性:对于任何非空引用 x,x.equals(x)
必须返回 true,说实话这条约定我不知道如何违反。
对称性:对于任何非空引用x和y,x.equals(y)的返回值必须与y.equals(x)的返回值相等,这需要如果你在一个类Student的equals方法中有对某个类Teenager的特殊比较,那Teenager的equals中也应该有对Student的特殊比较(除非你确认在集合中Teenager与Student的比较是想要的结果);或者father.equals(clid)返回true,clid.equals(father)返回false。
例:
import java.util.Objects;
public final class CaseInsensitiveString {
private final String s;
public CaseInsensitiveString(String s) {
this.s = Objects.requireNonNull(s);
}
// Broken - violates symmetry!
@Override
public boolean equals(Object o) {
if (o instanceof CaseInsensitiveString)
return s.equalsIgnoreCase(
((CaseInsensitiveString) o).s);
// 在这里对String的比较因为String没有相应的比较被称为非法尝试
if (o instanceof String) // One-way interoperability!
return s.equalsIgnoreCase((String) o);
return false;
}
...// Remainder omitted
}
传递性:如字面的意思,equals关系需要像平行关系一样具有传递性,在类中有继承关系需要注意避免子类clid1.equals(father1)返回true,clid2.equals(father1)返回true,clid1.equals(clid2)返回false的情况出现。
一致性:对相同的两个对象,多次调用equals的返回值必须保持一致,
良好的例子:
public final class PhoneNumber {
private final short areaCode, prefix, lineNum;
public PhoneNumber(int areaCode, int prefix, int lineNum) {
this.areaCode = rangeCheck(areaCode, 999, "area code");
this.prefix = rangeCheck(prefix, 999, "prefix");
this.lineNum = rangeCheck(lineNum, 9999, "line num");
}
private static short rangeCheck(int val, int max, String arg) {
if (val < 0 || val > max)
throw new IllegalArgumentException(arg + ": " + val);
return (short) val;
}
@Override
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof PhoneNumber))
return false;
PhoneNumber pn = (PhoneNumber) o;
// 对于非float、double类型的属性直接用==比较,float、double用对应封装类型的compare方法比较,其他引用类型调用equals方法比较
return pn.lineNum == lineNum && pn.prefix == prefix
&& pn.areaCode == areaCode;
}
... // Remainder omitted
}