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

11

197

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?

Scantling answered 27/2, 2009 at 20:14 Comment(2)
The expression x instanceof SomeClass is false if x is null. Hence, the second syntax doens't need the null check.Judge
@Judge Yes, the paragraph immediately after the code snippet says this as well. It is in the code snippet because that is what Eclipse generates.Scantling
F
111

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?

Fluviomarine answered 27/2, 2009 at 21:11 Comment(3)
exactly that came to my mind when i read the josh bloch quote above. +1 :)Constrain
definitely the key decision. symmetry must apply, and instanceof makes it very easy to be asymmetric accidentallySinclare
I'd also add that "if final seems restrictive" (on both equals and hashCode) then use getClass() equality instead of instanceof in order to preserve the symmetry and transitivity requirements of the equals contract.Jaan
T
192

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.

Tabasco answered 27/2, 2009 at 20:21 Comment(2)
The problem with the instanceof approach is that it breaks the "symmetric" property of Object.equals() in that it becomes possible that x.equals(y) == true but y.equals(x) == false if x.getClass() != y.getClass(). I prefer not to break this property unless absolutely necessary (i.e., overriding equals() for managed or proxy objects).Allcot
The instanceof approach is proper when, and only when, the base class defines what equality among subclass objects should mean. Using getClass does not violate the LSP, since the LSP merely relates to what can be done with existing instances--not what kinds of instances can be constructed. The class returned by getClass is an immutable property of an object instance. The LSP does not imply that it should be possible to create a subclass where that property indicates any class other than the one which created it.Ferryman
F
111

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?

Fluviomarine answered 27/2, 2009 at 21:11 Comment(3)
exactly that came to my mind when i read the josh bloch quote above. +1 :)Constrain
definitely the key decision. symmetry must apply, and instanceof makes it very easy to be asymmetric accidentallySinclare
I'd also add that "if final seems restrictive" (on both equals and hashCode) then use getClass() equality instead of instanceof in order to preserve the symmetry and transitivity requirements of the equals contract.Jaan
P
70

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.

Player answered 27/2, 2009 at 21:34 Comment(4)
That's the first clear and concise answer for this question. A code sample is worth a thousand words.Jellicoe
I was going through all other verbose answers util you saved the day.Simple, concise, elegant, and utterly the best answer of this question.Revere
There is the symmetry requirement as you mentioned. But there is also the "transitivity" requirement. If a.equals(c) and b.equals(c), then a.equals(b) (the naive approach of making Dog.equals just return super.equals(object) when !(object instanceof Dog) but checking the extra fields when it is a Dog instance wouldn't violate symmetry but would violate transitivity)Jaan
To be safe, you could use a getClass() equality check, or you could use an instanceof check if make your equals and hashCode methods final.Jaan
C
70

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.

Constrain answered 27/2, 2009 at 21:52 Comment(0)
G
28

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
Gilstrap answered 27/2, 2009 at 22:15 Comment(7)
Nothing about using getClass would violate the LSP, unless the base class specifically documented a means by which it should be possible to make instances of different subclasses which compare equal. What LSP violation do you see?Ferryman
Perhaps that should be rephrased as Use getClass and you leave subtypes in danger of LSP violations. Bloch has an example in Effective Java - also discussed here. His rationale is largely that it can result in surprising behavior for developers implementing subtypes that don't add state. I take your point - documentation is key.Gilstrap
The only time two instances of distinct classes should ever report themselves is equal is if they inherit from a common base class or interface which defines what equality means [a philosophy which Java applied, dubiously IMHO, to some collection interfaces]. Unless the base class contract says that getClass() should not be considered meaningful beyond the fact that the class in question is convertible to the base class, the return from getClass() should be considered like any other property which must match for instances to be equal.Ferryman
You mixed it! You meant exactly the opposite: Use getClass and you can never add significant members to subclasses ; Use instanceof and you violate the Liskov substitution principle.Festschrift
@Festschrift Where instanceof_ is used if a subclass added members to the equals contract this would violate the symmetric equality requirement. Using getClass subclasses can never be equal to the parent type. Not that you should be overriding equals anyway, but this is the trade-off if you do.Gilstrap
"Design and document for inheritance or prohibit it" I think is very important. My understanding is that the only time you should override equals is if you are creating an immutable value type, in which case the class should be declared final. Since there will be no inheritance, you are free to implement equals as needed. In the cases where you are allowing inheritance, then objects should compare by reference equality.Barbe
@TheSecretSquad: There is nothing wrong with having an inheritable type whose inheritance contract specifies that references which report themselves equals to each other must, from the class' point of view, behave interchangeably; such a contractual requirement would forbid encapsulated visible mutable state at any level of the inheritance hierarchy whether or not the class is final.Ferryman
A
25

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
Allegorist answered 26/10, 2011 at 5:3 Comment(0)
K
5

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; }
Kristinkristina answered 27/2, 2009 at 20:24 Comment(1)
Re your second point: He already mentioned that in the question. "I usually check the instanceof option, and then go in and remove the 'if (obj == null)' check."Tabasco
D
5

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.

Dunderhead answered 27/2, 2009 at 20:25 Comment(0)
E
3

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.

Ellswerth answered 27/2, 2009 at 20:33 Comment(0)
M
3

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.

Malaspina answered 27/2, 2009 at 22:6 Comment(0)
S
-1

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.

Scofflaw answered 1/12, 2015 at 13:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.