为什么我不能使用equals继承?

时间:2012-09-02 20:06:03

标签: java oop equals

当我读一本Java书时,作者已经说过,在设计一个类时,使用equals()继承通常是不安全的。例如:

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

在上面的课程中,我们应该放final,因此其他课程不能从中继承。我的问题是,为什么在允许另一个类从此继承时它是不安全的?

2 个答案:

答案 0 :(得分:21)

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

假设您有课程Vehicle和课程Car extends Vehicle。如果参数也是Vehicle.equals()且权重相同,则true会产生Vehicle。如果你想实现Car.equals(),只有当参数也是汽车时才会产生true,除了重量之外,它还应该比较make,engine等。

现在想象下面的代码:

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

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

你几乎没有解决方法:

  • strict:两个对象相等当且仅当它们具有完全相同的类型(且所有属性相等)时。这很糟糕,例如当你几乎没有子类添加一些行为或装饰原始类。一些框架也是你的类的子类,你没有注意到(Hibernate,带有CGLIB代理的Spring AOP ......)

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

    这可能会产生误导。取两个LinkedHashSet s(其中插入顺序是合同的一部分)。但是,由于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 :(得分:10)

Martin Odersky(Java中的泛型背后的人和当前javac的原始代码库)在他的书“ Scala中的编程”中有一个很好的章节来解决这个问题。他建议添加canEqual方法可以解决相等/继承问题。您可以在他的书的第一版中阅读讨论,该书可在线获取:

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

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

<强> 编辑:

看起来Odersky在2009年发表了一篇关于Java中相同概念的文章,它可以在同一个网站上找到:

How to Write an Equality Method in Java

我真的不认为在这个答案中试图总结一下这篇文章是否正确。它涵盖了对象平等的主题,从平等实现中的常见错误到作为等价关系的Java equals的完整讨论。你应该真的读它。