Различия между instanceof и Class.isAssignableFrom(…)
Когда речь заходит о проверке принадлежности объекта к определенному классу или интерфейсу в Java, можно использовать два метода: instanceof и Class.isAssignableFrom(…).
Алексей Кодов
Автор статьи
10 июля 2023 в 16:42
Когда речь заходит о проверке принадлежности объекта к определенному классу или интерфейсу в Java, можно использовать два метода: instanceof и Class.isAssignableFrom(. ) . Оба этих оператора используются для определения того, является ли объект экземпляром определенного класса или интерфейса. Но в чем же разница между ними?
Возьмем для примера следующий код:
Object a = new String("example"); boolean result = a instanceof String;
Object a = new String("example"); boolean result = String.class.isAssignableFrom(a.getClass());
В обоих случаях результат будет true , поскольку a действительно является экземпляром класса String .
Однако существуют различия в поведении этих двух операторов. Главное различие заключается в том, как они обрабатывают null значения.
Оператор instanceof возвращает false , если его левый операнд null . Это означает, что следующий код:
Object a = null; boolean result = a instanceof String;
вернет false , поскольку a равно null .
С другой стороны, метод Class.isAssignableFrom(. ) выбросит исключение NullPointerException , если ему передать null значение. Таким образом, следующий код:
Object a = null; boolean result = String.class.isAssignableFrom(a.getClass());
выбросит исключение NullPointerException , поскольку a.getClass() возвращает null .
Таким образом, в то время как instanceof безопасен для использования с null значениями, Class.isAssignableFrom(. ) требует предварительной проверки на null для избежания исключений. Это основное различие между instanceof и Class.isAssignableFrom(. ) .
instanceof or getClass()
An equals method needs to check the type of its argument, and there’s two ways to do that: with an instanceof check, or with a getClass() check.
public boolean equals(Object obj) if (!(obj instanceof Foo)) return false; // . > public boolean equals(Object obj) if (obj == null || getClass() != obj.getClass()) return false; // . >
(Note, with instanceof you don’t need an explicit null check, because instanceof already does that for you.)
Both are valid, but unfortunately, EqualsVerifier needs to know which one you use and it can’t always tell the difference. EqualsVerifier prefers instanceof , so if you use getClass() and run into a problem, you can add usingGetClass() :
EqualsVerifier.forClass(Foo.class) .usingGetClass() .verify();
Why the preference for instanceof ? Many IDEs generate equals methods with getClass() checks by default. Or maybe you just prefer getClass() over instanceof .
The reason is that Josh Bloch, in his Effective Java, recommends using instanceof over getClass() because instanceof plays nicer with the Liskov substitution principle. EqualsVerifier follows this advice.
You might ask: why is this Liskov substitution principle important? Well, tools like Hibernate, and many mocking frameworks such as Mockito and EasyMock, use bytecode manipulation tricks to generate subclasses on the fly. That means that an object that you instantiate yourself could never be equal to an object that you fetch from the database, even if all the fields have the same values, simply because they’re not of the same type.
If you use getClass() and you forget to add usingGetClass() , you might get this error message:
Subclass: object is not equal to an instance of a trivial subclass with equal fields: nl.jqno.equalsverifier.Foo@123456 Maybe you forgot to add usingGetClass(). Otherwise, consider making the class final or use EqualsVerifier.simple().
This is an example where the Liskov substitution principle and getClass() clash. EqualsVerifier creates a subclass of Foo , but adds nothing to it. Then it expects that equals treats it the same as an actual Foo . It doesn’t because getClass() returns different values for Foo and for the subclass.
In code, it looks like this:
class Foo private final int i; // . > class SubFoo extends Foo <> Foo a = new Foo(); Foo b = new SubFoo(); System.out.println(a.equals(b));
Should this print true or false ? SubFoo doesn’t change anything about Foo, so true seems to make most sense. However, with getClass() , it prints false .
Making Foo final is another way to avoid this problem.
Finally, if you think this is too strict, you can suppress Warning.STRICT_INHERITANCE or use EqualsVerifier.simple() instead.
If you want to know more about this, read Effective Java’s chapter on equals .
Updated: October 30, 2023
instanceof vs getClass in equals method in Java
As we use equals() and HashCode() methods while creating objects in Java, but most of us don’t know how to use them properly. In this post i’ll explain which to use and when.
Object.equals() provides a default implementation of equality-comparison and at times the default implementation is just fine and semantically correct for a newly defined class. In other situations the new class must override equals() to provide meaningful and semantically correct equality-comparison. instanceof approach is favoured when we use 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 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 in below example:
class B extends A
Object oA = new A(); Object oB = new B();
oA instanceof A => true oA instanceof B => false oB instanceof A => true // true
oA.getClass().equals(A.class) => true oA.getClass().equals(B.class) => false oB.getClass().equals(A.class) => false // true
Note: Use instanceof in the equals method if you cannot override it. Either make the method or the class final. Otherwise getClass() is the only way to go to guarantee that the contract for Object.equals is fulfilled.
Originally published at mybolder.com on December 1, 2015.
instanceof Vs getClass( )
Is there any guideline, which one to use getClass() or instanceOf ? Given a scenario: I know exact classes to be matched, that is String , Integer (these are final classes), etc. Is using instanceOf operator bad practise ?
15.5k 33 33 gold badges 114 114 silver badges 198 198 bronze badges
asked Feb 14, 2011 at 7:40
3,925 2 2 gold badges 30 30 silver badges 44 44 bronze badges
This is explained in: stackoverflow.com/questions/596462/….
Feb 14, 2011 at 7:43
Your timing method is causing artificial delays and producing incorrect timing results. Swap the order you do the checks and you’ll see the first check you do (either == or instanceof) will always be longer. I’d guess it’s the println()s. You should never include that stuff in your timing block.
Sep 14, 2012 at 18:30
Just one comment apart, to compare performance, use multiple cycle iterations (e.g. 10000) in order to improve accuracy. One single invocation is not a good measure.
Mar 18, 2014 at 10:28
How large is the difference?
Dec 16, 2020 at 14:30
4 Answers 4
The reason that the performance of instanceof and getClass() == . is different is that they are doing different things.
- instanceof tests whether the object reference on the left-hand side (LHS) is an instance of the type on the right-hand side (RHS) or some subtype.
- getClass() == . tests whether the types are identical.
So the recommendation is to ignore the performance issue and use the alternative that gives you the answer that you need.
Is using the instanceOf operator bad practice ?
Not necessarily. Overuse of either instanceOf or getClass() may be «design smell». If you are not careful, you end up with a design where the addition of new subclasses results in a significant amount of code reworking. In most situations, the preferred approach is to use polymorphism.
However, there are cases where these are NOT «design smell». For example, in equals(Object) you need to test the actual type of the argument, and return false if it doesn’t match. This is best done using getClass() .
Terms like «best practice», «bad practice», «design smell», «antipattern» and so on should be used sparingly and treated with suspicion. They encourage black-or-white thinking. It is better to make your judgements in context, rather than based purely on dogma; e.g. something that someone said is «best practice». I recommend that everyone read No Best Practices if they haven’t already done so.