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

hqt*_*hqt 11 java oop equals

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

public final class Date {
    public boolean equals(Object o) {
         // some code here
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Tom*_*icz 22

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

说你有班级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
Run Code Online (Sandbox Code Playgroud)

true如果巧合油箱和公共汽车具有相同的重量,则第一次比较可能会产生.但由于坦克不是汽车,因此将它与汽车相比将永远屈服false.

你有一些解决方法:

  • strict:当且仅当两个对象具有完全相同的类型(并且所有属性相等)时,它们是相等的.这很糟糕,例如,当你几乎没有子类添加一些行为或装饰原始类时.一些框架也是你的类的子类,你没有注意到(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));
    
    Run Code Online (Sandbox Code Playgroud)


Dao*_*Wen 10

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

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

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

编辑:

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

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

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