What is the difference between instanceof and Class.isAssignableFrom(...)?
Which of the following is better?
a instanceof B
or
B.class.isAssignableFrom(a.getClass())
The only difference that I know of is, when 'a' is null, the first returns false, while the second throws an exception. Other than that, do they always give the same result?
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 error
3 instanceof Foo // syntax error
int.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:
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.Object
instanceof foo/A
JAVA:
A.class.isInstance(b);
Bytecode:
ldc Lfoo/A; (org.objectweb.asm.Type)
getstatic foo/Benchmark.b:java.lang.Object
invokevirtual 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.Object
invokevirtual java/lang/Object getClass(()Ljava/lang/Class;);
invokevirtual java/lang/Class isAssignableFrom((Ljava/lang/Class;)Z);
Measuring how many bytecode instructions is 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 operator.
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 Units
Benchmark.testIsInstance thrpt 2000 373,061 ± 0,115 ops/us
Benchmark.testInstanceOf thrpt 2000 371,047 ± 0,131 ops/us
Benchmark.testIsAssignableFrom thrpt 2000 363,648 ± 0,289 ops/us
Warning
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 code
for (int i = 0; i < 100; ++i)
execute();
// Time it
int 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:
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
上一篇: 如何检查Java中是否存在文件?