В своей книге "Эффективная Java" Джошуа Блох пишет о подводных камнях, которые происходят с контрактом equals()
, когда производные классы добавляют дополнительные поля к проверке. Обычно это нарушает симметрию, но Блох утверждает, что "вы можете добавить компонент значения в подкласс абстрактного класса, не нарушая равный контракт".
Очевидно, это верно, потому что не может быть экземпляров абстрактного класса, поэтому нет никакой симметрии для нарушения. Но как насчет других подклассов? Я написал этот пример, намеренно исключая реализации hashcode и null-проверок, чтобы держать код коротким:
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 ()
вообще в абстрактном классе.