Any reason to prefer getClass() over instanceof wh

2019-01-01 03:37发布

问题:

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?

回答1:

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.



回答2:

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.



回答3:

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.



回答4:

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.



回答5:

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


回答6:

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


回答7:

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.



回答8:

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


回答9:

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.



回答10:

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.



回答11:

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.