Java .equals()instanceof子类?为什么不调用超类等于而不是最终?

时间:2013-09-02 14:17:15

标签: java equals instanceof

在对象的.equals(Object) javadoc:

中说明
  

它是对称的:对于任何非空参考值x和y,   当且仅当y.equals(x)返回时,x.equals(y)才应返回true   真。

在示例代码中几乎无处不在我看到覆盖.equals(Object)方法,它使用instanceof作为首批测试之一,例如:What issues / pitfalls must be considered when overriding equals and hashCode?

public class Person {
    private String name;
    private int age;

    public boolean equals(Object obj) {
        if (obj == null)
            return false;
        if (obj == this)
            return true;
        if (!(obj instanceof Person))
            return false;
        ...
    }

}

现在class SpecialPerson extends Person位于equals

        if (!(obj instanceof SpecialPerson))
            return false;

我们不保证.equals()是对称的。 例如,这里已经讨论过:any-reason-to-prefer-getclass-over-instanceof-when-generating-equals

Person a = new Person(), b = new SpecialPerson();

a.equals(b);    //sometimes true, since b instanceof Person
b.equals(a);    //always false

也许我应该在SpecialPerson的开头添加等于直接调用super?

    public boolean equals(Object obj) {
        if( !obj instanceof SpecialPerson )
            return super.equals(obj);
        ... 
        /* more equality tests here */
    }

5 个答案:

答案 0 :(得分:9)

许多示例使用instanceof有两个原因:a)它将空检查折叠并将check键入一个或b)该示例适用于Hibernate或其他一些代码重写框架。

“正确”(根据JavaDoc)解决方案是使用this.getClass() == obj.getClass()。这适用于Java,因为类是单例,VM保证这一点。如果你是偏执狂,你可以使用this.getClass().equals(obj.getClass()),但这两者实际上是等价的。

大部分时间都可以使用。但有时,Java框架需要使用字节代码做“聪明”的事情。这通常意味着它们会自动创建子类型。由于子类型应该被认为与原始类型相同,equals()必须以“错误”的方式实现,但这无关紧要,因为在运行时,子类型都将遵循某些模式。例如,他们会在调用setter之前做其他事情。这对“平等”没有影响。

正如您所注意到的,当您遇到这两种情况时,事情开始变得丑陋:您真正扩展了基本类型,并将其与自动子类型生成混合在一起。如果这样做,则必须确保永远不要使用非叶类型。

答案 1 :(得分:1)

你在这里遗漏了一些东西。我将尝试强调这一点:

假设您有Person person = new Person()Person personSpecial = new SpecialPerson(),那么我相信您不希望这两个对象相等。所以,它真的按要求工作,等于必须返回false。

此外,对称性指定两个类中的equals()方法必须同时遵守它。如果一个等于返回true而其他返回false,那么我会说这个缺陷在等于覆盖。

答案 2 :(得分:0)

您尝试解决问题的方法不正确。假设您有2个子类SpecialPersonBizarrePerson。通过此实现,BizarrePerson个实例可以等于SpecialPerson个实例。你通常不希望这样。

答案 3 :(得分:0)

不要使用instanceof。请改用this.getClass() == obj.getClass()。那么你正在检查这个确切的类。

使用equals时,您应该始终使用hashCode并覆盖它!

Person的hashCode方法可能如下所示:

@Override
public int hashCode()
{
    final int prime = 31;
    int result = 1;
    result = prime * result + age;
    result = prime * result + ((name == null) ? 0 : name.hashCode());
    return result;
}

并在你的equals方法中使用它:

if (this.hashCode() != obj.hashCode())
{
    return false;
}

答案 4 :(得分:0)

类型不应该认为自己等于任何其他类型的对象 - 甚至是子类型 - ,除非两个对象派生自一个公共类,其合约指定不同类型的后代应该如何检查相等性

例如,抽象类StringyThing可以封装字符串,并提供执行转换为字符串或提取子字符串等方法的方法,但不对支持格式强加任何要求。例如,StringyThing的一个可能的子类型可能包含StringyThing的数组,并封装所有这些字符串的串联值。如果转换为字符串会产生相同的结果,则StringyThing的两个实例将被定义为相等,并且两个其他无法区分的StringyThing实例之间的比较可能不得不依赖于它们,但是StringyThing - 派生类型可以包括优化各种情况的代码。例如,如果一个StringyThing表示“M重复字符ch”而另一个表示“N重复字符串St”,后一个类型知道第一个,它可以检查St是否只包含M/N重复的字符ch。这样的检查将指示字符串是否相等,而不必“扩展”它们中的任何一个。