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

发布于 2024-07-14 11:17:22 字数 663 浏览 5 评论 0原文

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

不使用instanceof

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

使用instanceof

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

我一般都会勾选instanceof选项,然后进去去掉“if (obj = = null)”检查。 (这是多余的,因为 null 对象总是会失败 instanceof。)有什么理由认为这是一个坏主意吗?

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?

Without using instanceof:

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

Using instanceof:

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

I usually check the instanceof option, 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

金兰素衣 2024-07-21 11:17:22

Josh Bloch 赞成你的方法:

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

另请参阅这个答案

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

Josh Bloch favors your approach:

The reason that I favor the instanceof approach is that when you use the getClass approach, 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.

See also this SO answer.

Effective Java chapter 3 also covers this.

永言不败 2024-07-21 11:17:22

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


我在这里想要表达的是,如果您认为 getClass() 是保持对称性的唯一可靠方法,那么您可能会以错误的方式使用 equals() 。

当然,使用 getClass() 来保留 equals() 所需的对称性很容易,但这只是因为 x.equals(y) 和y.equals(x) 始终为 false。 里氏可替换性会鼓励您找到一种保持对称性的实现,在有意义时可以产生 true。 如果一个子类具有完全不同的平等概念,那么它真的是一个子类吗?

If you use instanceof, making your equals implementation final will preserve the symmetry contract of the method: x.equals(y) == y.equals(x). If final seems restrictive, carefully examine your notion of object equivalence to make sure that your overriding implementations fully maintain the contract established by the Object class.


What I'm trying to get at here is that if you believe getClass() is the only reliable way to preserve symmetry, you are probably using equals() the wrong way.

Sure, it's easy to use getClass() to preserve the symmetry required of equals(), but only because x.equals(y) and y.equals(x) are always false. Liskov substitutability would encourage you to find a symmetry-preserving implementation that can yield true when it makes sense. If a subclass has a radically different notion of equality, is it really a subclass?

椵侞 2024-07-21 11:17:22

Angelika Langers 平等的秘密 深入探讨对一些常见且众所周知的例子(包括 Josh Bloch 和 Barbara Liskov)进行了长时间而详细的讨论,发现了大多数例子中的一些问题。 她还深入了解了 instanceofgetClass。 其中的一些引述

结论

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

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

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

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

Angelika Langers Secrets of equals gets 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 instanceof vs getClass. Some quote from it

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.

时间你老了 2024-07-21 11:17:22

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

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

通过使用instanceof,有可能不对称。 考虑这个例子:
狗延伸了动物。
Animal 的 equals 对 Animal 进行 instanceof 检查。
Dog 的 equals 对 Dog 进行 instanceof 检查。
给 Animal a 和 Dog d (其他字段相同):

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

这违反了对称性。

为了严格遵循 equal 契约,必须保证对称性,因此类需要相同。

The reason to use getClass is to ensure the symmetric property of the equals contract. From 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.

By using instanceof, it's possible to not be symmetric. Consider the example:
Dog extends Animal.
Animal's equals does an instanceof check of Animal.
Dog's equals does an instanceof check of Dog.
Give Animal a and Dog d (with other fields the same):

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.

此刻的回忆 2024-07-21 11:17:22

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

  • 使用instanceof,您永远无法向子类添加重要成员。
  • 使用getClass就违反了里氏替换原则。

布洛赫Effective Java 第二版

  • 第 17 项:继承或的设计和文档禁止它

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

  • Use instanceof and you can never add significant members to subclasses.
  • Use getClass and you violate the Liskov substitution principle.

Bloch has another relevant piece of advice in Effective Java Second Edition:

  • Item 17: Design and document for inheritance or prohibit it
誰ツ都不明白 2024-07-21 11:17:22

如果我错了,请纠正我,但是当您想确保您的实例不是您要比较的类的子类时, 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

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:

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
忆伤 2024-07-21 11:17:22

如果您想确保仅该类匹配,请使用 getClass() == 。 如果你想匹配子类,那么需要instanceof

另外,instanceof 不会与 null 匹配,但与 null 进行比较是安全的。 所以你不必对它进行空检查。

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

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

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.

if ( ! (obj instanceof MyClass) ) { return false; }
晌融 2024-07-21 11:17:22

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

class LastName
{
(...)
}


class FamilyName
extends LastName
{
(..)
}

在这里我会使用“instanceof”,因为我想要将 LastName 与 FamilyName 进行比较,

class Organism
{
}

class Gorilla extends Organism
{
}

在这里我会使用“getClass”,因为该类已经表明这两个实例不相等。

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

class Organism
{
}

class Gorilla extends Organism
{
}

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

策马西风 2024-07-21 11:17:22

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

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

ArryaList 和 RoleList 都是 instanceof 列表

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

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

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

instanceof works for instences of the same class or its subclasses

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 instanceof List

While

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

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.

┈┾☆殇 2024-07-21 11:17:22

这两种方法都有其问题。

如果子类改变了身份,那么你需要比较它们的实际类。 否则,就会违反对称性。 例如,不同类型的 Person 不应被视为等效,即使它们具有相同的名称。

但是,某些子类不会更改标识,并且这些子类需要使用 instanceof。 例如,如果我们有一堆不可变的 Shape 对象,那么长度和宽度为 1 的 Rectangle 应该等于单位 Square

在实践中,我认为前一种情况更有可能成立。 通常,子类化是你身份的基本组成部分,并且与你的父母一模一样,除非你能做一件小事,但这并不意味着你就是平等的。

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.

However, some subclasses don't change identity and these need to use instanceof. For instance, if we have a bunch of immutable Shape objects, then a Rectangle with length and width of 1 should be equal to the 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.

一梦浮鱼 2024-07-21 11:17:22

实际上,instanceof 检查对象是否属于某个层次结构。 例如:Car 对象属于 Vehical 类。 因此“Vehical 的 new Car() 实例”返回 true。 而“new Car().getClass().equals(Vehical.class)”返回 false,虽然 Car 对象属于 Vehical 类,但它被归类为单独的类型。

Actually instanceof check where an object belongs to some hierarchy or not. ex: Car object belongs to Vehical class. So "new Car() instance of Vehical" returns true. And "new Car().getClass().equals(Vehical.class)" return false, though Car object belongs to Vehical class but it's categorized as a separate type.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文