Java 在生成 .equals() 时,有什么理由更喜欢 getClass() 而不是 instanceof?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/596462/
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-11 16:40:28  来源:igfitidea点击:

Any reason to prefer getClass() over instanceof when generating .equals()?

javaeclipseequalsinstanceof

提问by Kip

I'm using Eclipse to generate .equals()and .hashCode(), and there is an option labeled "Use 'instanceof' to compare types". The default is for this option to be unchecked and use .getClass()to compare types. Is there any reason I should prefer .getClass()over instanceof?

我正在使用 Eclipse 生成.equals().hashCode(),并且有一个标记为“使用 'instanceof' 来比较类型”的选项。默认情况下,此选项未选中并用于.getClass()比较类型。有什么理由我应该喜欢.getClass()instanceof

Without using instanceof:

不使用instanceof

if (obj == null)
  return false;
if (getClass() != obj.getClass())
  return false;

Using instanceof:

使用instanceof

if (obj == null)
  return false;
if (!(obj instanceof MyClass))
  return false;

I usually check the instanceofoption, and then go in and remove the "if (obj == null)" check. (It is redundant since null objects will always fail instanceof.) Is there any reason that's a bad idea?

我通常选中该instanceof选项,然后进入并删除“ if (obj == null)”检查。(这是多余的,因为空对象总是会失败instanceof。)有什么理由认为这是个坏主意?

采纳答案by erickson

If you use instanceof, making your equalsimplementation finalwill preserve the symmetry contract of the method: x.equals(y) == y.equals(x). If finalseems restrictive, carefully examine your notion of object equivalence to make sure that your overriding implementations fully maintain the contract established by the Objectclass.

如果您使用instanceof,则使您的equals实现final将保留方法的对称契约:x.equals(y) == y.equals(x)。如果final看起来有限制,请仔细检查您的对象等价概念,以确保您的覆盖实现完全维护Object类建立的契约。

回答by Michael Myers

Josh Blochfavors your approach:

Josh Bloch赞成你的方法:

The reason that I favor the instanceofapproach is that when you use the getClassapproach, you have the restriction that objects are only equal to other objects of the same class, the same run time type. If you extend a class and add a couple of innocuous methods to it, then check to see whether some object of the subclass is equal to an object of the super class, even if the objects are equal in all important aspects, you will get the surprising answer that they aren't equal. In fact, this violates a strict interpretation of the Liskov substitution principle, and can lead to very surprising behavior. In Java, it's particularly important because most of the collections (HashTable, etc.) are based on the equals method. If you put a member of the super class in a hash table as the key and then look it up using a subclass instance, you won't find it, because they are not equal.

我赞成这种instanceof方法的原因是,当你使用这种getClass方法时,你有一个限制,即对象只能等于相同类、相同运行时类型的其他对象。如果你扩展一个类并给它添加一些无害的方法,然后检查子类的某个对象是否等于超类的某个对象,即使这些对象在所有重要方面都相等,你也会得到令人惊讶的答案是它们不相等。事实上,这违反了对Liskov 替换原则的严格解释,并可能导致非常令人惊讶的行为。在 Java 中,这尤其重要,因为大多数集合 (HashTable等)基于 equals 方法。如果将超类的一个成员放在哈希表中作为键,然后使用子类实例查找它,您将找不到它,因为它们不相等。

See also this SO answer.

另请参阅此 SO 答案

Effective Java chapter 3also covers this.

Effective Java第 3 章也涵盖了这一点。

回答by Clint

If you want to ensure only that class will match then use getClass() ==. If you want to match subclasses then instanceofis needed.

如果您想确保只有该类匹配,请使用getClass() ==. 如果要匹配子类,则instanceof需要。

Also, instanceof will not match against a null but is safe to compare against a null. So you don't have to null check it.

此外, instanceof 不会与 null 匹配,但可以安全地与 null 进行比较。所以你不必空检查它。

if ( ! (obj instanceof MyClass) ) { return false; }

回答by Pierre

It depends if you consider if a subclass of a given class is equals to its parent.

这取决于您是否考虑给定类的子类是否等于其父类。

class LastName
{
(...)
}


class FamilyName
extends LastName
{
(..)
}

here I would use 'instanceof', because I want a LastName to be compared to FamilyName

在这里,我将使用“instanceof”,因为我希望将姓氏与 FamilyName 进行比较

class Organism
{
}

class Gorilla extends Organism
{
}

here I would use 'getClass', because the class already says that the two instances are not equivalent.

在这里,我将使用“getClass”,因为该类已经说这两个实例不等价。

回答by OscarRyz

instanceofworks for instences of the same class orits subclasses

instanceof适用于同一类或其子类的实例

You can use it to test if an object is an instance of a class, an instance of a subclass, or an instance of a class that implements a particular interface.

您可以使用它来测试对象是类的实例、子类的实例还是实现特定接口的类的实例。

ArryaList and RoleList are both instanceofList

ArryaList 和 RoleList 都是List 的实例

While

尽管

getClass() == o.getClass()will be true only if both objects ( this and o ) belongs to exactly the same class.

仅当两个对象( this 和 o )属于完全相同的类时,getClass() == o.getClass()才会为真。

So depending on what you need to compare you could use one or the other.

因此,根据您需要比较的内容,您可以使用其中一种。

If your logic is: "One objects is equals to other only if they are both the same class" you should go for the "equals", which I think is most of the cases.

如果您的逻辑是:“只有当它们都是同一类时,一个对象才等于另一个对象”,那么您应该选择“相等”,我认为这是大多数情况。

回答by Steve Kuo

The reason to use getClassis to ensure the symmetric property of the equalscontract. From equals' JavaDocs:

使用的原因getClass是为了保证equals合约的对称性。来自 equals 的 JavaDocs:

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。

By using instanceof, it's possible to not be symmetric. Consider the example: Dog extends Animal. Animal's equalsdoes an instanceofcheck of Animal. Dog's equalsdoes an instanceofcheck of Dog. Give Animal aand Dog d(with other fields the same):

通过使用 instanceof,可能不是对称的。考虑这个例子:Dog 扩展了Animal。Animal's对 Animalequals进行instanceof检查。Dog'sequalsinstanceof检查 Dog。给 Animal a和 Dog d(其他字段相同):

a.equals(d) --> true
d.equals(a) --> false

This violates the symmetric property.

这违反了对称性质。

To strictly follow equal's contract, symmetry must be ensured, and thus the class needs to be the same.

要严格遵守equal的约定,必须保证对称,因此类必须相同。

回答by Johannes Schaub - litb

Angelika Langers Secrets of equalsgets into that with a long and detailed discussion for a few common and well-known examples, including by Josh Bloch and Barbara Liskov, discovering a couple of problems in most of them. She also gets into the instanceofvs getClass. Some quote from it

Angelika Langers Secrets of equals深入讨论了一些常见和众所周知的例子,包括 Josh Bloch 和 Barbara Liskov,他们发现了其中的大部分问题。她也进入了instanceofvs getClass。一些引用它

Conclusions

Having dissected the four arbitrarily chosen examples of implementations of equals() , what do we conclude?

First of all: there are two substantially different ways of performing the check for type match in an implementation of equals() . A class can allow mixed-type comparison between super- and subclass objects by means of the instanceof operator, or a class can treat objects of different type as non-equal by means of the getClass() test. The examples above illustrated nicely that implementations of equals() using getClass() are generally more robust than those implementations using instanceof .

The instanceof test is correct only for final classes or if at least method equals() is final in a superclass. The latter essentially implies that no subclass must extend the superclass's state, but can only add functionality or fields that are irrelevant for the object's state and behavior, such as transient or static fields.

Implementations using the getClass() test on the other hand always comply to the equals() contract; they are correct and robust. They are, however, semantically very different from implementations that use the instanceof test. Implementations using getClass() do not allow comparison of sub- with superclass objects, not even when the subclass does not add any fields and would not even want to override equals() . Such a "trivial" class extension would for instance be the addition of a debug-print method in a subclass defined for exactly this "trivial" purpose. If the superclass prohibits mixed-type comparison via the getClass() check, then the trivial extension would not be comparable to its superclass. Whether or not this is a problem fully depends on the semantics of the class and the purpose of the extension.

结论

在剖析了四个任意选择的 equals() 实现示例之后,我们得出什么结论?

首先:在 equals() 的实现中执行类型匹配检查有两种截然不同的方法。一个类可以通过 instanceof 运算符允许超类和子类对象之间的混合类型比较,或者一个类可以通过 getClass() 测试将不同类型的对象视为不相等。上面的例子很好地说明了使用 getClass() 的 equals() 实现通常比使用 instanceof 的那些实现更健壮。

instanceof 测试仅适用于最终类,或者如果至少方法 equals() 在超类中是最终的。后者本质上意味着没有子类必须扩展超类的状态,而只能添加与对象的状态和行为无关的功能或字段,例如瞬态或静态字段。

另一方面,使用 getClass() 测试的实现总是遵守 equals() 契约;他们是正确和稳健的。然而,它们在语义上与使用 instanceof 测试的实现非常不同。使用 getClass() 的实现不允许将子类与超类对象进行比较,即使子类没有添加任何字段,甚至不想覆盖 equals() 也是如此。例如,这种“琐碎”的类扩展将是在为这个“琐碎”目的而定义的子类中添加调试打印方法。如果超类通过 getClass() 检查禁止混合类型比较,那么平凡的扩展将无法与其超类进行比较。这是否是一个问题完全取决于类的语义和扩展的目的。

回答by James

Both methods have their problems.

这两种方法都有其问题。

If the subclass changes the identity, then you need to compare their actual classes. Otherwise, you violate the symmetric property. For instance, different types of Persons should not be considered equivalent, even if they have the same name.

如果子类更改了身份,则需要比较它们的实际类。否则,您违反了对称属性。例如,不同类型的Persons 不应被视为等效,即使它们具有相同的名称。

However, some subclasses don't change identity and these need to use instanceof. For instance, if we have a bunch of immutable Shapeobjects, then a Rectanglewith length and width of 1 should be equal to the unit Square.

然而,一些子类不会改变身份,这些需要使用instanceof. 例如,如果我们有一堆不可变Shape对象,那么Rectangle长度和宽度为 1 的 a 应该等于 unit Square

In practice, I think the former case is more likely to be true. Usually, subclassing is a fundamental part of your identity and being exactly like your parent except you can do one little thing does not make you equal.

在实践中,我认为前一种情况更可能是真实的。通常,子类化是您身份的基本组成部分,并且与您的父母完全一样,只是您可以做一件小事并不能使您平等。

回答by McDowell

This is something of a religious debate. Both approaches have their problems.

这是一场宗教辩论。这两种方法都有其问题。

  • Use instanceofand you can never add significant members to subclasses.
  • Use getClassand you violate the Liskov substitution principle.
  • 使用instanceof并且您永远不能向子类添加重要成员。
  • 使用getClass并且您违反了 Liskov 替换原则。

Blochhas another relevant piece of advice in Effective Java Second Edition:

BlochEffective Java Second Edition 中有另一个相关的建议:

  • Item 17: Design and document for inheritance or prohibit it
  • 第 17 条:设计和文件继承或禁止

回答by TraderJoeChicago

Correct me if I am wrong, but getClass() will be useful when you want to make sure your instance is NOT a subclass of the class you are comparing with. If you use instanceof in that situation you can NOT know that because:

如果我错了,请纠正我,但是当您想确保您的实例不是您要比较的类的子类时, getClass() 将很有用。如果您在这种情况下使用 instanceof,您将无法知道这一点,因为:

class A { }

class B extends A { }

Object oA = new A();
Object oB = new B();

oA instanceof A => true
oA instanceof B => false
oB instanceof A => true // <================ HERE
oB instanceof B => true

oA.getClass().equals(A.class) => true
oA.getClass().equals(B.class) => false
oB.getClass().equals(A.class) => false // <===============HERE
oB.getClass().equals(B.class) => true