Java .equals() instanceof 子类?为什么不调用超类 equals 而不是使其成为 final?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18575294/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 09:08:56  来源:igfitidea点击:

Java .equals() instanceof subclass? Why not call superclass equals instead of making it final?

javaequalsinstanceof

提问by user2622016

It is stated in Object's .equals(Object)javadoc:

它在 Object 的.equals(Object)javadoc 中说明:

It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.

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

Almost everywhere in example code I see overridden .equals(Object)method which uses instanceofas one of the first tests, for example here: What issues / pitfalls must be considered when overriding equals and hashCode?

在示例代码中几乎无处不在,我看到被覆盖的.equals(Object)方法instanceof用作第一个测试之一,例如在这里:覆盖 equals 和 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;
        ...
    }

}

}

Now with class SpecialPerson extends Personhaving in equals:

现在,随着class SpecialPerson extends Person具有equals

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

we con not guarantee that .equals()is symmetric. It has been discussed for example here: any-reason-to-prefer-getclass-over-instanceof-when-generating-equals

我们不保证它.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

Maybe I should add in the beginning of SpecialPerson's equals direct call to super?

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

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

采纳答案by Aaron Digulla

A lot of the examples use instanceoffor two reasons: a) it folds the null check and type check into one or b) the example is for Hibernate or some other code-rewriting framework.

许多示例instanceof出于两个原因使用:a) 它将空检查和类型检查合并为一个或 b) 该示例用于 Hibernate 或其他一些代码重写框架。

The "correct" (as per the JavaDoc) solution is to use this.getClass() == obj.getClass(). This works for Java because classes are singletons and the VM guarantees this. If you're paranoid, you can use this.getClass().equals(obj.getClass())but the two are really equivalent.

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

This works most of the time. But sometimes, Java frameworks need to do "clever" things with the byte code. This usually means they create a subtype automatically. Since the subtype should be considered equal to the original type, equals()must be implemented in the "wrong" way but this doesn't matter since at runtime, the subtypes will all follow certain patterns. For example, they will do additional stuff before a setter is being called. This has no effect on the "equalness".

这在大多数情况下都有效。但有时,Java 框架需要用字节码做“聪明”的事情。这通常意味着它们会自动创建子类型。由于子类型应被视为与原始类型相同,因此equals()必须以“错误”的方式实现,但这并不重要,因为在运行时,子类型都将遵循某些模式。例如,他们会在调用 setter 之前做一些额外的事情。这对“平等”没有影响。

As you noticed, things start to get ugly when you have both cases: You really extend the base types and you mix that with automatic subtype generation. If you do that, you must make sure that you never use non-leaf types.

正如您所注意到的,当您同时拥有这两种情况时,事情就会变得很糟糕:您确实扩展了基本类型,并将其与自动子类型生成混合在一起。如果你这样做,你必须确保你永远不会使用非叶类型。

回答by JB Nizet

Your attempt at solving the problem is not correct. Suppose you have 2 subclasss SpecialPersonand BizarrePerson. With this implementation, BizarrePersoninstances could be equal to SpecialPersoninstances. You generally don't want that.

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

回答by dharam

You are missing something here. I will try to highlight this:

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

Suppose you have Person person = new Person()and Person personSpecial = new SpecialPerson()then I am sure you would not like these two objects to be equal. So, its really working as required, the equal must return false.

假设您有Person person = new Person()Person personSpecial = new SpecialPerson()然后我确定您不希望这两个对象相等。因此,它确实按要求工作,equal 必须返回 false。

Moreover, symmetry specifies that the equals()method in both the classes must obey it at the same time. If one equals return true and other return false, then I would say the flaw is in the equals overriding.

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

回答by Philipp Sander

don't use instanceof. use this.getClass() == obj.getClass()instead. then you are checking for this exact class.

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

when working with equalsyou should always use the hashCodeand override that too!

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

the hashCode method for Person could look like this:

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;
}

and use it like this in your equals method:

并在您的 equals 方法中像这样使用它:

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

回答by supercat

A type should not consider itself equal to an object of any other type--even a subtype--unless both objects derive from a common class whose contract specifies how descendants of different types should check for equality.

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

For example, an abstract class StringyThingcould encapsulate strings, and provide methods to do things like convert to a string or extract substrings, but not impose any requirements on the backing format. One possible subtype of StringyThing, for example, might contain an array of StringyThingand encapsulate the value of the concatenation of all those strings. Two instances of StringyThingwould be defined as equal if conversion to strings would yield identical results, and comparison between two otherwise-indistinguishable StringyThinginstances whose types knew nothing about each other may have to fall back on that, but StringyThing-derived types could include code to optimize various cases. For example, if one StringyThingrepresents "Mrepetitions of character ch" and another represents "Nrepetitions of the string St", and the latter type knows about the first, it could check whether Stcontains nothing but M/Nrepetitions of the character ch. Such a check would indicate whether or not the strings are equal, without having to "expand out" either one of them.

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