当你的类具有逻辑意义上的等同性的内涵(即使类的实例的标识不同但其内容是一样的,那么它们也是相等的),你就必须重写equals方法。这样的类一般是携带值的类(value classes)。重写equals方法要遵循以下contract:
1. Reflexivity
2. Symmetry
3. Transitivity
4. Consistency
按下面的steps可写出高质量的equals方法:
1. 用 "==" 检查参数是否是指向本对象的一个引用
2. 用 "instanceof" 检查参数的类型是否和本对象的类型一样
3. 把参数转换到正确的类型
4. 检测参数的字段和本对象的相应字段是否匹配相等
如果这些步骤都成功,就返回true.
Item 9: Always override hasCode when you override equals
Object的技术规范指出,如果两个对象根据equals方法比较的结果是等同的,那么这个两个对象的hashCode方法必须返回相同的整数值。这样在把这样的对象作为基于hash的map时,才不会在存取数据时出现问题。查看一下HashMap.put方法的代码:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entrye = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
put同时用到key的hashCode和equals方法。显然,e.hash == hash && ((k = e.key) == key || key.equals(k))这个条件和两个对象等同则其hash必须一样的协约吻合。
编写一个好的hashCode方法也是有规可循的。书中给出了很具体的操作要点。下面是书中给出的一个例子:
public final class PhoneNumber {
private final short areaCode;
private final short prefix;
private final short lineNumber;
//omitted some lines
@Override
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof PhoneNumber))
return false;
PhoneNumber pn = (PhoneNumber)o;
return pn.lineNumber == lineNumber
&& pn.prefix == prefix
&& pn.areaCode == areaCode;
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + areaCode;
result = 31 * result + prefix;
result = 31 * result + lineNumber;
return result;
}
}
17是随意的一个数,31则是一个传统的选择,但要最好是一个质数。
没有评论:
发表评论