java - 从抽象类派生时如何遵守equals()的契约

标签 java oop inheritance

在他的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/

相关文章:

java - 如何直接从 Jackson JSON 中的 ObjectMapper 写入 JSON 对象(ObjectNode)?

java - 深层链接无法在三星手机上运行(当我尝试从默认信使打开它时)

c++ - 将类视为一流对象

oop - 观察者模式还是回调?

c++ - 更改继承中构造函数的顺序

Java静态变量值不能传递

java - 在 Java 中使用 GSSAPI 身份验证时,来自 Active Directory 的搜索结果中的 LDAP Continuation Reference 错误

java - 使用 jni.h 在 C++ 中编译 java 方法时生成错误

java - 将多个 Java 对象数组传递给另一个方法

c# - Entity Framework 6 (EF6) 中每个具体类型的表 (TPC) 继承