2012-09-02 46 views
10

当我读到一本Java书时,作者曾经说过,在设计一个类时,使用equals()来继承是非常不安全的。例如:为什么我不应该用继承来使用equals?

public final class Date { 
    public boolean equals(Object o) { 
     // some code here 
    } 
} 

在上面的类,我们应该把final,所以其他类不能从这个继承。而我的问题是,为什么当它允许另一个类继承它是不安全的?

+0

'Comparable'指定'compareTo',而不是'equals'。 – oldrinb

+0

哦。谢谢:)我编辑过:D – hqt

+0

这本书叫什么名字? –

回答

19

因为很难(不可能?)使它正确,尤其是symmetric property

假设您有Vehicle类和Car extends Vehicle类。 Vehicle.equals()如果参数也是Vehicle并且具有相同的权重,则产生true。如果你想实现Car.equals()它应该产生true仅当参数也是一个车,除了重量外,还应做比较,发动机等

现在想象一下下面的代码:

Vehicle tank = new Vehicle(); 
Vehicle bus = new Car(); 
tank.equals(bus); //can be true 
bus.equals(tank); //false 

如果通过重合坦克和公共汽车具有相同的重量,第一次比较可能产生true。但由于坦克不是汽车,因此将它与汽车相比总会产生false

你有一些变通办法:

  • 严格:两个对象相等当且仅当他们有完全相同的类型(以及所有属性都相等)。这很糟糕,例如当你仅仅为了添加一些行为或装饰原始类而进行子类化时。一些框架也在不需要注意的情况下继承类(Hibernate,使用CGLIB代理的Spring AOP ...)

  • 松散:如果两个对象的类型“兼容”并且它们具有相同内容(语义上),则两个对象相等。例如。如果它们包含相同的元素,则两个集合是相等的,一个是HashSet而另一个是TreeSet(感谢@veer用于指出)并不重要。

    这可能会引起误解。取两个LinkedHashSet(其中插入顺序作为合同的一部分)。然而,由于equals()只需要原料Set合同考虑,比较收益率true即使是明显不同的对象:

    Set<Integer> s1 = new LinkedHashSet<Integer>(Arrays.asList(1, 2, 3)); 
    Set<Integer> s2 = new LinkedHashSet<Integer>(Arrays.asList(3, 2, 1)); 
    System.out.println(s1.equals(s2)); 
    

又见

+1

不*不可能*;见例如['Set.equals'](http://docs.oracle.com/javase/7/docs/api/java/util/Set.html#equals(java.lang.Object)) – oldrinb

+0

@veer:我更新了我的回答,谢谢。 –

+0

@TomaszNurkiewicz作为你的链接给我,如果我们使用if(this.getClass()!= tank.getClass()){return false;}或if(this.getClass()!= tank.getClass() ){return false;}',问题就解决了。你能告诉我更多关于这个案子吗? – hqt

8

马丁·奥德斯基(中Java中泛型的背后人物以及当前的原始代码库)在他的书中有一个很好的章节编程Scala解决这个问题。他建议添加一个canEqual方法可以解决相等/继承问题。你可以在他的书的第一版,这是可以在线阅读的讨论:

Chapter 28 of Programming in Scala, First Edition: Object Equality

这本书当然是指斯卡拉的,但同样的想法适用于经典的Java。对于来自Java背景的人来说,示例源代码不应太难理解。

编辑:

它看起来像Odersky的早在2009年发表了在Java中同一概念的文章,它可在同一网站上:

How to Write an Equality Method in Java

我真的不要以为在这个答案中总结这篇文章会做正义。它涵盖了深入讨论对象平等的主题,从平等实现中的常见错误到Java equals作为等价关系的全面讨论。你应该真的只是读它。

相关问题