为什么我不能使用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
作为等价关系。 你应该真正阅读它。