在他的Effective Java 一书中,Joshua Bloch 描述了当派生类向检查中添加额外字段时,equals()
的约定会出现的陷阱。通常,这会破坏对称性,但 Bloch 指出“您可以将值组件添加到抽象类的子类,而不会违反 equals 契约”。
显然这是真的,因为不能有抽象类的实例,所以不存在可违反的对称性。但是其他子类呢?我写了这个例子,故意省略哈希码实现和空检查以保持代码简短:
public abstract class Vehicle {
private final String color;
public Vehicle(String color) {
this.color = color;
}
public String getColor() {
return color;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Vehicle)) return false;
Vehicle that = (Vehicle) o;
return color.equals(that.color);
}
}
public class Bicycle extends Vehicle {
public Bicycle(String color) {
super(color);
}
}
public class Car extends Vehicle {
private final String model;
public Car(String color, String model) {
super(color);
this.model = model;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Car)) return false;
Car that = (Car) o;
return getColor().equals(that.getColor()) && model.equals(that.model);
}
}
当我用相同的颜色字符串为每个类创建一个实例时,equals()
的对称性被打破了:
Bicycle bicycle = new Bicycle("blue");
Car car = new Car("blue", "Mercedes");
bicycle.equals(car) <- true
car.equals(bicycle) <- false
我不确定如何以最佳方式处理此问题。在抽象类中将 equals()
声明为抽象以在子类中强制实现?但是在抽象类中完全不声明 equals ()
也可以达到同样的效果。
最佳答案
Java 的 equals 契约在这种情况下变得特别参差不齐,最终这一切都变成了程序员的偏好和需求的问题。我记得遇到过同样的问题,我遇到了 this article ,在考虑与 Java 的平等契约时讨论了几种可能性和问题。它基本上最终会说,如果不违反 Java 平等契约,就没有办法正确地做到这一点。
在处理抽象 类时,我个人的偏好是根本不给抽象类一个equals 方法。这没有意义。你不能有两个抽象类型的对象;你应该如何比较它们?相反,我为每个子类提供了自己的 equals,只要 equals()
被调用,运行时就会处理其余部分。总的来说,我最常遵循的文章中提出的解决方案是“只能比较完全相同类的对象”,这对我来说似乎是最明智的。
关于java - 从抽象类派生时如何遵守equals()的契约,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32931030/