在Java中覆盖equals和hashCode时应该考虑哪些问题?
覆盖和时必须考虑哪些问题/陷阱?equals
hashCode
覆盖和时必须考虑哪些问题/陷阱?equals
hashCode
equals()
(javadoc)必须定义等价关系(它必须是自反的、对称的和传递的)。此外,它必须是一致的(如果对象未被修改,那么它必须继续返回相同的值)。此外,必须始终返回 false。o.equals(null)
hashCode()
(javadoc)也必须是一致的(如果对象未在 方面进行修改,则必须继续返回相同的值)。equals()
这两种方法之间的关系是:
只要
a.等于 (b)
,则a.hashCode()
必须与b.hashCode() 相同
。
如果覆盖一个,则应覆盖另一个。
使用用于计算的同一组字段来计算 。equals()
hashCode()
使用 Apache Commons Lang 库中优秀的帮助器类 EqualsBuilder 和 HashCodeBuilder。例如:
public class Person {
private String name;
private int age;
// ...
@Override
public int hashCode() {
return new HashCodeBuilder(17, 31). // two randomly chosen prime numbers
// if deriving: appendSuper(super.hashCode()).
append(name).
append(age).
toHashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Person))
return false;
if (obj == this)
return true;
Person rhs = (Person) obj;
return new EqualsBuilder().
// if deriving: appendSuper(super.equals(obj)).
append(name, rhs.name).
append(age, rhs.age).
isEquals();
}
}
使用基于哈希的集合或映射(如 HashSet、LinkedHashSet、HashMap、Hashtable 或 WeakHashMap)时,请确保放入集合中的键对象的 hashCode() 在对象位于集合中时永远不会更改。确保这一点的防弹方法是使您的密钥不可变,这还有其他好处。
如果您正在处理使用对象关系映射器(ORM)(如Hibernate)持久化的类,那么有一些问题值得注意,如果您不认为这已经非常复杂了!
延迟加载的对象是子类
如果您的对象是使用 ORM 持久化的,那么在许多情况下,您将处理动态代理,以避免过早地从数据存储中加载对象。这些代理作为您自己的类的子类实现。这意味着 将返回 。例如:this.getClass() == o.getClass()
false
Person saved = new Person("John Doe");
Long key = dao.save(saved);
dao.flush();
Person retrieved = dao.retrieve(key);
saved.getClass().equals(retrieved.getClass()); // Will return false if Person is loaded lazy
如果你正在处理一个ORM,使用o instanceof Person
是唯一可以正确操作的东西。
延迟加载的对象具有空字段
ORM 通常使用 getter 来强制加载延迟加载的对象。这意味着这将是如果是延迟加载,即使强制加载并返回“John Doe”。根据我的经验,这在 和 中更频繁地出现。person.name
null
person
person.getName()
hashCode()
equals()
如果您正在处理ORM,请确保始终使用getters,并且永远不要在hashCode()
和equals()
中字段引用。
保存对象将更改其状态
持久性对象通常使用字段来保存对象的键。首次保存对象时,此字段将自动更新。不要在 中使用 id 字段。但是您可以在.id
hashCode()
equals()
我经常使用的一种模式是
if (this.getId() == null) {
return this == other;
}
else {
return this.getId().equals(other.getId());
}
但是:您不能包含在 .如果这样做,当对象持久化时,其将发生变化。如果对象在 中,您将“永远不会”再次找到它。getId()
hashCode()
hashCode
HashSet
在我的例子中,我可能会使用for和plus(仅用于偏执狂)作为。如果有一些“碰撞”的风险是可以的,但对于 .Person
getName()
hashCode
getId()
getName()
equals()
hashCode()
equals()
hashCode()
应使用 equals()
中的属性的不可更改子集