当我读一本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,但同样的想法适用于经典Java.对于来自Java背景的人来说,样本源代码不应该太难理解.
编辑:
看起来Odersky在2009年发表了一篇关于Java中相同概念的文章,它可以在同一个网站上找到:
我真的不认为试图在这个答案中总结一下这篇文章是否正确.它涵盖了对象平等的主题,从平等实现中的常见错误到Java equals作为等价关系的完整讨论.你真的应该读它.