What is the difference between instanceof and Class.isAssignableFrom(...)? What is the difference between instanceof and Class.isAssignableFrom(...)? java java

What is the difference between instanceof and Class.isAssignableFrom(...)?


When using instanceof, you need to know the class of B at compile time. When using isAssignableFrom() it can be dynamic and change during runtime.


instanceof can only be used with reference types, not primitive types. isAssignableFrom() can be used with any class objects:

a instanceof int  // syntax error3 instanceof Foo  // syntax errorint.class.isAssignableFrom(int.class)  // true

See http://java.sun.com/javase/6/docs/api/java/lang/Class.html#isAssignableFrom(java.lang.Class).


Talking in terms of performance :

TL;DR

Use isInstance or instanceof which have similar performance. isAssignableFrom is slightly slower.

Sorted by performance:

  1. isInstance
  2. instanceof (+ 0.5%)
  3. isAssignableFrom (+ 2.7%)

Based on a benchmark of 2000 iterations on JAVA 8 Windows x64, with 20 warmup iterations.

In theory

Using a soft like bytecode viewer we can translate each operator into bytecode.

In the context of:

package foo;public class Benchmark{  public static final Object a = new A();  public static final Object b = new B();  ...}

JAVA:

b instanceof A;

Bytecode:

getstatic foo/Benchmark.b:java.lang.Objectinstanceof foo/A

JAVA:

A.class.isInstance(b);

Bytecode:

ldc Lfoo/A; (org.objectweb.asm.Type)getstatic foo/Benchmark.b:java.lang.Objectinvokevirtual java/lang/Class isInstance((Ljava/lang/Object;)Z);

JAVA:

A.class.isAssignableFrom(b.getClass());

Bytecode:

ldc Lfoo/A; (org.objectweb.asm.Type)getstatic foo/Benchmark.b:java.lang.Objectinvokevirtual java/lang/Object getClass(()Ljava/lang/Class;);invokevirtual java/lang/Class isAssignableFrom((Ljava/lang/Class;)Z);

Measuring how many bytecode instructions are used by each operator, we could expect instanceof and isInstance to be faster than isAssignableFrom. However, the actual performance is NOT determined by the bytecode but by the machine code (which is platform dependent). Let's do a micro benchmark for each of the operators.

The benchmark

Credit: As advised by @aleksandr-dubinsky, and thanks to @yura for providing the base code, here is a JMH benchmark (see this tuning guide):

class A {}class B extends A {}public class Benchmark {    public static final Object a = new A();    public static final Object b = new B();    @Benchmark    @BenchmarkMode(Mode.Throughput)    @OutputTimeUnit(TimeUnit.MICROSECONDS)    public boolean testInstanceOf()    {        return b instanceof A;    }    @Benchmark    @BenchmarkMode(Mode.Throughput)    @OutputTimeUnit(TimeUnit.MICROSECONDS)    public boolean testIsInstance()    {        return A.class.isInstance(b);    }    @Benchmark    @BenchmarkMode(Mode.Throughput)    @OutputTimeUnit(TimeUnit.MICROSECONDS)    public boolean testIsAssignableFrom()    {        return A.class.isAssignableFrom(b.getClass());    }    public static void main(String[] args) throws RunnerException {        Options opt = new OptionsBuilder()                .include(TestPerf2.class.getSimpleName())                .warmupIterations(20)                .measurementIterations(2000)                .forks(1)                .build();        new Runner(opt).run();    }}

Gave the following results (score is a number of operations in a time unit, so the higher the score the better):

Benchmark                       Mode   Cnt    Score   Error   UnitsBenchmark.testIsInstance        thrpt  2000  373,061 ± 0,115  ops/usBenchmark.testInstanceOf        thrpt  2000  371,047 ± 0,131  ops/usBenchmark.testIsAssignableFrom  thrpt  2000  363,648 ± 0,289  ops/us

Warning

  • the benchmark is JVM and platform dependent. Since there are no significant differences between each operation, it might be possible to get a different result (and maybe different order!) on a different JAVA version and/or platforms like Solaris, Mac or Linux.
  • the benchmark compares the performance of "is B an instance of A" when "B extends A" directly. If the class hierarchy is deeper and more complex (like B extends X which extends Y which extends Z which extends A), results might be different.
  • it is usually advised to write the code first picking one of the operators (the most convenient) and then profile your code to check if there are a performance bottleneck. Maybe this operator is negligible in the context of your code, or maybe...
  • in relation to the previous point, instanceof in the context of your code might get optimized more easily than an isInstance for example...

To give you an example, take the following loop:

class A{}class B extends A{}A b = new B();boolean execute(){  return A.class.isAssignableFrom(b.getClass());  // return A.class.isInstance(b);  // return b instanceof A;}// Warmup the codefor (int i = 0; i < 100; ++i)  execute();// Time itint count = 100000;final long start = System.nanoTime();for(int i=0; i<count; i++){   execute();}final long elapsed = System.nanoTime() - start;

Thanks to the JIT, the code is optimized at some point and we get:

  • instanceof: 6ms
  • isInstance: 12ms
  • isAssignableFrom : 15ms

Note

Originally this post was doing its own benchmark using a for loop in raw JAVA, which gave unreliable results as some optimization like Just In Time can eliminate the loop. So it was mostly measuring how long did the JIT compiler take to optimize the loop: see Performance test independent of the number of iterations for more details

Related questions