我为什么不应该使用具有继承等于?(Why should I not use equals with

2019-06-27 17:00发布

当我读了一本Java书,作者曾表示,设计一个类时,它通常不安全使用equals()与继承。 例如:

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

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

Answer 1:

因为它是硬(不可能?)作出正确选择,尤其是对称性

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

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

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

第一个比较可能产生true ,如果凑巧罐和公交车具有相同的权重。 但由于油箱是不是汽车,相对于一个汽车总是会产生false

你有几个变通办法:

  • 严格:两个对象如果相等且仅当它们拥有完全相同的类型(以及所有属性都相等)。 这是不好的,例如,当你继承勉强添加一些行为或装饰的原始类。 一些框架是子类化你的类以及没有你注意到(休眠,Spring AOP的使用CGLIB代理...)

  • 宽松:如果他们的类型是“兼容”,他们有相同的内容(语义)两个对象是相等的。 例如,两个集合相等,如果它们包含相同的元素,它并不重要,一个是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)); 


Answer 2:

马丁·奥德斯基(在Java泛型和原来的代码库当前背后的人javac在斯卡拉在他的书中编程一个不错的章解决这一问题。 他建议增加一个canEqual方法可以解决平等/继承问题。 您可以阅读他的书,这是网上提供的第一版的讨论:

在Scala中,第一版编程的第28章:对象平等

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

编辑:

它看起来像Odersky的早在2009年发表了在Java中同一概念的文章,而且是适用相同的网站:

如何用Java编写的平等法

我真的不认为试图总结这个答案的文章会做到公正。 它深入探讨了对象平等的话题,从平等的实现作出Java的充分讨论常见的错误, equals为等价关系。 你真的应该只是阅读。



文章来源: Why should I not use equals with inheritance?
标签: java oop equals