2013-04-26 17 views
23

假设有两类这样的:当superclass不重新声明equals()和hashCode()时会出现什么问题?

abstract class A { /* some irrelevant methods */ } 

class B extends A { 
    public final int x; 

    public B(final int x) { 
     this.x = x; 
    } 

    /* some more irrelevant methods */ 
} 

然后我产生对Bequals()hashCode()方法使用Eclipse的 “源→生成的hashCode()和equals()......”。但Eclipse警告我:

超类'com.example.test2.A'不重新声明equals()和hashCode() - 生成的代码可能无法正常工作。

那么,什么会导致生成的代码与生成的方法无法正常工作?


(顺便说一句,生成的方法是这样的:

@Override 
public int hashCode() { 
    final int prime = 31; 
    int result = 1; 
    result = prime * result + x; 
    return result; 
} 

@Override 
public boolean equals(Object obj) { 
    if (this == obj) 
     return true; 
    if (obj == null) 
     return false; 
    if (getClass() != obj.getClass()) 
     return false; 
    B other = (B) obj; 
    if (x != other.x) 
     return false; 
    return true; 
} 

+1

这可能是有趣的。这就是说,我不确定它是如何适用的,因为A是抽象的。 http://www.artima.com/lejava/articles/equality.html。大多数情况下,它试图阻止这个'b.equals(a)',但'!a.equals(b)' – SJuan76 2013-04-26 12:47:33

+2

我认为你的问题已经受到了大量文献的影响,这些文献解释了一个非常相似但却截然不同的情景。下面的几乎每个答案都会谈到超类有一个“等号”方法,而在你的例子中,它显然不是。我甚至不是提问者,我在这里感到沮丧! – 2013-04-26 18:48:17

+0

@KennyTM是否仍然生成警告,如果您删除抽象修饰符? – Mike 2013-04-26 19:10:29

回答

5

我认为这项警告是错误的。我所见过的关于equals施工(包括布洛赫的第8项)的每一篇文献都警告说,在父级确实实现等于的情况下。

鉴于您的A课程仅使用参考平等,所以我无法了解您的方法违反任何所需原则(对称性,传递性和反身性)的情况。

请记住,任何半合理equals方法将有一个类型检查。这是自动生成的代码的真正的原题:

if (getClass() != obj.getClass()) 
    return false; 

而不是仅仅引用Bloch的书和其他网站的数据块,我们应该想到漫长而艰难的是否有任何的“标准”等于问题如果父类不实现equals,可能。我会说他们不是,我欢迎反例。

+0

超类A没有实现equals,所以子类B实现它(通过检查'id',说)因此,可能存在b.equals(a)因为'id'匹配的实例,但!!.e.equals(b)因为他们是不同的对象。 – SJuan76 2013-04-26 14:08:59

+0

@ SJuan76只有使用编码较差的'equals'方法才不会比较类的类型。 – 2013-04-26 18:39:39

+0

查看我提供的链接,以获取更好的解释示例,了解如何在不太糟糕的编码实现中发生。 – SJuan76 2013-04-26 20:26:22

9

当重写等于坚持一组特定的规则时,您必须小心。详细信息请参见javadoc。总之,两个棘手的部分是保持对称性和传递性。据约书亚座的Effective Java

“有没有办法扩展了新类并添加值组件,同时保证equal”

这是什么意思?好吧,让我们假设你在A类具有类型T型五他人财产的,并在子类B中的财产,如果这两个类重写equals那么A比乙比较,B到A的时候,你会得到不同的结果

A a = new A(T obj1); 
B b = new B(T obj1, V obj2); 
a.equals(b) //will return true, because objects a and b have the same T reference. 
b.equals(a) //will return false because a is not an instanceof B 

这是违反对称性的。如果您尝试通过混合比较来纠正此问题,您将失去传递性。

B b2 = new B(T obj1, V obj3); 
b.equals(a) // will return true now, because we altered equals to do mixed comparisions 
b2.equals(a) // will return true for the same reason 
b.equals(b2) // will return false, because obj2 != obj3 

在这种情况下,b == a,b2 == a,b!= b2,这是个问题。

编辑

在努力更准确地回答这个问题:“会做出什么生成的代码不会生成的方法正常工作,”让我们考虑这种特殊情况下。父类是抽象的,不会覆盖equals。我相信我们可以得出结论,守则是安全的,没有发生违反平等合同的情况。这是父类被抽象的结果。它不能被实例化,因此上面的例子不适用于它。

现在考虑父类是具体的并且不覆盖等于的情况。正如邓肯琼斯指出的那样,警告信息仍然生成,在这种情况下似乎是正确的。默认情况下,全部类继承自Object,并将根据对象标识(即内存地址)进行比较。如果与覆盖等于的子类一起使用,这可能导致不对称比较。

A a = new A(); 
B b = new B(T obj1); 
a.equals(b) //will return false, because the references do not point at the same object 
b.equals(a) //should return false, but could return true based on implementation logic. 

如果b.equals(一)返回true,无论出于何种原因,无论是实现逻辑或编程错误,将导致对称性被破坏。编译器无法执行此操作,因此会生成警告。

+3

查看这个链接http://www.artima.com/lejava/articles/equality.html。基本上,子类可以通过在那里提出的机制确保superInstance.equals(subInstance)为false。 – SJuan76 2013-04-26 14:05:07

+0

*“无法扩展一个可实例化的类......”* - 但类“A”是抽象的。 – kennytm 2013-04-26 14:14:20

+0

如果这与警告是真实/相关的,那么如果超类实现了等于或者不是,则会得到警告。 – SJuan76 2013-04-26 14:34:05

-3

为了让人信服的理由等了一段时间,我就拿走了。

我联系的文章(再次,它是http://www.artima.com/lejava/articles/equality.html)解释了为什么在子类中实现平等的时候(主要是,该instanceof检查导致了由equals合同禁止asimmetries)的问题。

如果你的超类没有实现equals,你可能会发现这种情况。

  • B扩展一个

  • B实现equals,可以说像

    public boolean equals(Object obj) { 
        A a = (A) obj; <-- this cast is the most problematic issue from this example 
        return this.id.equals(a.getId()); 
    } 
    

所以你

A a = new A("Hello"); 
B b = new B("Hello"); 
a.equals(b) != b.equals(a); 

一个点会违背这个结束例子是你的超类是抽象的,但是可能Eclipse会在预防中发出警告,让你实例化它(或者只是警告检查不是很细致)。

+0

由于A是摘要,eclipse不会为此提供警告。警告的实际原因见我的回答 – 2013-04-26 15:18:09

+1

我认为你的等于例子是不必要的穷人。为什么'B'类中的equals方法将对象转换为'A'?任何明智的平等方法的核心都是阶级检查。我觉得你已经按照你的榜样去适应你认为的答案。 – 2013-04-26 18:43:31

+1

@Duncan我很确定你是对的(尽管没有人同意你的观点)。没有实现'equals'的超类肯定没有问题('Object'也没有实现它)。重写一个不重要的'equals'可能是一个问题,正如文章中所解释的那样,但它们提供了一个通用的解决方案('canEqual')。不幸的是,这篇文章写得相当混乱。 – maaartinus 2014-06-15 18:01:11

0

如果父类和子类都实现equals这样一种方式,两个引用不可能被认为是相等的,除非两个引用的实例都是同一个确切的类,否则不会有任何与继承有关的问题equals

当有可能具有不同类别的实例但仍应该比较相等时,就会出现问题。处理这种情况的唯一正确方法是指定任何可能比较相等的实例必须来自一个公共类,并且该类的合约必须指定什么样的平等意味着什么。通用类equals(其可能或可能不是抽象的)通常会定义派生类可以覆盖的虚拟成员以测试相等的不同方面。例如,如果普通类equals方法类似于if (other==null) return false; else return this.equals2(other) && other.equals2(this);,那么将基本上保证equals2的任何实现的对称行为,这不会改变所比较的对象。

相关问题