重写equals和hashCode时必须考虑哪些问题/陷阱?
当前回答
超类中有两个方法,如java.lang.Object。我们需要将它们重写为自定义对象。
public boolean equals(Object obj)
public int hashCode()
相等的对象只要相等就必须产生相同的哈希码,然而不相等的对象不需要产生不同的哈希码。
public class Test
{
private int num;
private String data;
public boolean equals(Object obj)
{
if(this == obj)
return true;
if((obj == null) || (obj.getClass() != this.getClass()))
return false;
// object must be Test at this point
Test test = (Test)obj;
return num == test.num &&
(data == test.data || (data != null && data.equals(test.data)));
}
public int hashCode()
{
int hash = 7;
hash = 31 * hash + num;
hash = 31 * hash + (null == data ? 0 : data.hashCode());
return hash;
}
// other methods
}
如果你想了解更多,请点击这个链接http://www.javaranch.com/journal/2002/10/equalhash.html
这是另一个例子, http://java67.blogspot.com/2013/04/example-of-overriding-equals-hashcode-compareTo-java-method.html
玩得开心!@ .@
其他回答
在检查成员是否相等之前,有几种方法可以检查类是否相等,我认为这两种方法在适当的情况下都是有用的。
使用instanceof操作符。 使用this.getClass () .equals (that.getClass())。
我在最终的等号实现中使用#1,或者在实现指定等号算法的接口时使用#1。util集合接口-检查with (obj instanceof Set)或任何你正在实现的接口的正确方法)。当等号可以被重写时,这通常是一个糟糕的选择,因为这会破坏对称性。
选项#2允许安全地扩展类,而不重写等号或破坏对称性。
如果你的类也是Comparable类,equals和compareTo方法也应该是一致的。下面是Comparable类中equals方法的模板:
final class MyClass implements Comparable<MyClass>
{
…
@Override
public boolean equals(Object obj)
{
/* If compareTo and equals aren't final, we should check with getClass instead. */
if (!(obj instanceof MyClass))
return false;
return compareTo((MyClass) obj) == 0;
}
}
对于平等的人,可以看看Angelika Langer的《平等的秘密》。我非常喜欢它。她还提供了关于Java泛型的常见问题解答。在这里查看她的其他文章(向下滚动到“核心Java”),在那里她还继续介绍了第2部分和“混合类型比较”。祝你阅读愉快!
关于obj.getClass() != getClass()的澄清。
此语句是equals()不友好继承的结果。JLS (Java语言规范)指定如果A.equals(B) == true,那么B.equals(A)也必须返回true。如果省略该语句,继承重写equals()(并改变其行为)的类将破坏此规范。
考虑下面的例子,当语句被省略时会发生什么:
class A {
int field1;
A(int field1) {
this.field1 = field1;
}
public boolean equals(Object other) {
return (other != null && other instanceof A && ((A) other).field1 == field1);
}
}
class B extends A {
int field2;
B(int field1, int field2) {
super(field1);
this.field2 = field2;
}
public boolean equals(Object other) {
return (other != null && other instanceof B && ((B)other).field2 == field2 && super.equals(other));
}
}
做新A(1)= (new A(1))同样,new B(1,1)。equals(new B(1,1))结果给出true,就像它应该的那样。
这看起来很好,但是看看如果我们尝试使用这两个类会发生什么:
A a = new A(1);
B b = new B(1,1);
a.equals(b) == true;
b.equals(a) == false;
显然,这是错误的。
如果你想确保对称条件。如果b=a,则a=b,而利斯科夫替换原理不仅在b实例中调用super.equals(other),而且在a实例中检查:
if (other instanceof B )
return (other != null && ((B)other).field2 == field2 && super.equals(other));
if (other instanceof A) return super.equals(other);
else return false;
它将输出:
a.equals(b) == true;
b.equals(a) == true;
其中,如果a不是B的引用,那么它可能是类a的引用(因为您扩展了它),在这种情况下,也可以调用super.equals()。
逻辑上我们有:
a.getClass().equals(b.getClass()) && a.equals(b) ⇒ a.hashCode() == b.hashCode()
但反之亦然!
我发现的一个问题是两个对象包含彼此的引用(一个例子是父/子关系,在父对象上有一个方便的方法来获取所有的子对象)。 例如,在执行Hibernate映射时,这类事情相当常见。
如果在hashCode或equals测试中包含关系的两端,则有可能进入以StackOverflowException结束的递归循环。 最简单的解决方案是在方法中不包括getChildren集合。
推荐文章
- codestyle;把javadoc放在注释之前还是之后?
- 如何在Spring中定义List bean ?
- 将Set<T>转换为List<T>的最简洁的方法
- 在JavaScript中,什么相当于Java的Thread.sleep() ?
- 使用Java重命名文件
- URL从Java中的类路径加载资源
- .toArray(new MyClass[0]) or .toArray(new MyClass[myList.size()])?
- Hibernate中不同的保存方法之间有什么区别?
- Java 8流和数组操作
- Java Regex捕获组
- Openssl不被视为内部或外部命令
- 如何添加自定义方法到Spring Data JPA
- 如何在Ubuntu中设置Java环境路径
- 无法执行dex:在Eclipse中超过GC开销限制
- 有人能解释一下JPA和Hibernate中的mappedBy吗?