为什么我不能使用equalsinheritance?

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

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

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

因为很难(不可能?)使它正确,特别是对称属性 。

假设您有Vehicle和class级Car extends Vehicle 。 如果参数也是Vehicle且具有相同的权重,则Vehicle.equals()产生true 。 如果你想实现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 (其中插入顺序是合同的一部分)。 但是,由于equals()仅考虑原始Set契约,因此即使对于明显不同的对象,比较也会产生true

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

也可以看看

  • Java基础知识第2部分:平等和inheritance

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

Scala编程第28章,第1版:对象平等

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

编辑:

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

如何在Java中编写一个等式方法

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