在 Java 中使用继承平等有什么问题?

What's wrong with using Inheritance Equality in Java?

在我正在阅读的“Core Java Volume 1”一书中说,相等不应该与继承一起使用。所以,我有下面的例子,它似乎有问题:

public class Main {

    public static void main(String[] args) {

        C c = new C("Test", 10);
        D d = new D("Test", 10);

        if (c.equals(d))
            System.out.println("Equal");
        else
            System.out.println("Unequal");

        if (d.equals(c))
            System.out.println("Equal");
        else
            System.out.println("Unequal");
    }
}


class C
{
    C(String cstr, int cnum) {
        str = cstr;
        num = cnum;
    }

    @Override
    public boolean equals(Object otherObject) {

        // A quick test to see if the objects are identical.
        if (this == otherObject) {
            return true;
        }

        // Must return false if the explicit parameter is null
        if (otherObject == null)
        {
            return false;
        }

        if (!(otherObject instanceof C))
            return false;

        // Now we know otherObject is a non-null Employee
        C other = (C) otherObject;

        // Test whether the fields have identical values
        return str.equals(other.str) && num == other.num;
    }

    private String str;
    private int num;
}

class D extends C {

    D(String cstr, int cnum) {
        super(cstr, cnum);
    }
}

http://ideone.com/PhFBwG

它 returns "Equal" 用于两个对称比较,这大概是不应该的。它是否在派生的 class 'D' 中缺少另一个 equals 方法?如果是这样,那么如果我根本不使用另一个 equals 派生它,上面的示例代码会有什么问题?

如果您不希望实例 CD 比较相等,请将 !(otherObject instanceof C) 替换为 otherObject.getClass() != this.getClass()。这将确保只有 C 可以等于另一个 C 并且只有 D 可以等于另一个 D.

这是否是您想要的取决于几个因素。我的一般经验法则是,"open-ended" 等式 class 可以添加 "external users"(无论对您意味着什么)都不是一个好主意。如果你想让不同类型的对象比较相等(这种情况很少见),那么应该严格控制层次结构(例如 final classes)。

集合(和类似的定义良好的接口)是不同的:这里的接口定义了一个非常精确的契约,所有实现 classes 都需要遵循(即一个 ArrayList 可以等于一个LinkedList 如果它们都包含相同顺序的相同元素,因为 List 接口定义了两个 List 相等的含义)。

该代码有效,因为正如您所说,D 不会覆盖 equals。我希望这本书的意思是 D 应该 覆盖 equals (并且它们都应该覆盖 hashCode),因为 D 实例不应该等于 C 实例,因为它们是不同的(尽管相关)类型。

请记住 the equals contract 的关键方面之一是 对称性 。对于 cd 的任何非 null 值,如果 c.equals(d) 为真,则 d.equals(c) 必须为真(反之亦然)。虽然在您当前的代码中确实如此,但可能不应该如此,因为 DC 是不同的类型。如果 D 个实例确实 等同于 C 个实例,为什么要有 D