instanceof和Class.isAssignableFrom(…)有什么区别?
以下哪一项比较好?
a instanceof B
要么
B.class.isAssignableFrom(a.getClass())
我所知道的唯一区别是,当'a'为空时,第一个返回false,而第二个抛出exception。 除此之外,他们总是给出相同的结果吗?
使用instanceof
,需要在编译时知道B
的类。 当使用isAssignableFrom()
它可以是dynamic的,并在运行时更改。
instanceof
只能用于引用types,而不能用于原始types。 isAssignableFrom()
可以用于任何类对象:
a instanceof int // syntax error 3 instanceof Foo // syntax error int.class.isAssignableFrom(int.class) // true
请参阅http://java.sun.com/javase/6/docs/api/java/lang/Class.html#isAssignableFrom(java.lang.Class); 。
在性能方面谈话:
TL; DR
使用具有相似性能的isInstance或instanceof 。 isAssignableFrom稍微慢一些。
按性能sorting:
- isInstance
- instanceof (+ 0.5%)
- isAssignableFrom (+ 2.7%)
基于JAVA 8 Windows x64的2000次迭代基准,进行了20次热身迭代。
理论上
使用类似字节码的软件,我们可以将每个操作符转换成字节码。
在以下方面:
package foo; public class Benchmark { public static final Object a = new A(); public static final Object b = new B(); ... }
JAVA:
b instanceof A;
字节码:
getstatic foo/Benchmark.b:java.lang.Object instanceof foo/A
JAVA:
A.class.isInstance(b);
字节码:
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());
字节码:
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);
测量每个运算符使用多less字节码指令,我们可以预期instanceof和isInstance比isAssignableFrom快。 然而,实际的性能不是由字节码来决定的,而是由机器码决定(取决于平台)。 让我们为每个运营商做一个微观基准。
基准
学分:正如@ aleksandr-dubinsky所build议的,并感谢@yura提供的基本代码,这是一个JMH基准testing(请参阅本调整指南 ):
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(); } }
得到以下结果(得分是一个时间单位的一些操作 ,所以得分越高越好):
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
警告
- 基准是JVM和平台相关的。 由于每个操作之间没有显着差异,因此可能会在不同的JAVA版本和/或像Solaris,Mac或Linux平台上获得不同的结果(也可能是不同的顺序!)。
- 当“B扩展A”时,基准比较“B是A的一个实例”的性能。 如果类层次更深和更复杂(如B扩展X扩展Y,扩展Z扩展A),结果可能会不同。
- 通常build议先编写代码先select一个操作符(最方便),然后对代码进行configuration,以检查是否存在性能瓶颈。 也许这个运算符在你的代码的上下文中是微不足道的,或者也许…
- 关于以前的观点,你的代码上下文中的
instanceof
可能比isInstance
更容易被优化,例如…
给你一个例子,采取以下循环:
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;
由于JIT,代码在某个时候被优化了,我们得到:
- instanceof:6ms
- isInstance:12ms
- isAssignableFrom:15ms
注意
最初这篇文章是使用原始JAVA中的for循环做自己的基准testing,由于像Just In Time这样的一些优化可以消除循环,所以给出了不可靠的结果。 所以这主要是测量JIT编译器花费多less时间来优化循环:请参阅独立于迭代次数的性能testing以获取更多详细信息
相关问题
- instanceof运算符是否会产生很多开销? 为什么?
- 如何在JAVA内部实现instanceof?
- 在Java中使用instanceof的性能影响
更直接的等价于a instanceof B
B.class.isInstance(a)
这个工作(返回false),当a
也是null
。
除了上面提到的基本差异之外,instanceof操作符和isAssignableFrom方法在Class中还有一个核心的细微区别。
读取instanceof
为“是这个(左边部分)这个或这个(右边部分)的任何子类的实例”,并且读取x.getClass().isAssignableFrom(Y.class)
为“我可以写X x = new Y()
“。 换句话说,instanceof运算符检查左对象是否是相同类或右类的子类,而isAssignableFrom
检查是否可以将参数类(from)的对象分配给调用该方法的类的引用。
请注意,这两个都认为实际的实例不是引用types。
考虑三个A,B和C类的例子,其中C扩展B,B扩展A.
B b = new C(); System.out.println(b instanceof A); //is b (which is actually class C object) instance of A, yes. This will return true. System.out.println(b instanceof B); // is b (which is actually class C object) instance of B, yes. This will return true. System.out.println(b instanceof C); // is b (which is actually class C object) instance of C, yes. This will return true. If the first statement would be B b = new B(), this would have been false. System.out.println(b.getClass().isAssignableFrom(A.class));//Can I write C c = new A(), no. So this is false. System.out.println(b.getClass().isAssignableFrom(B.class)); //Can I write C c = new B(), no. So this is false. System.out.println(b.getClass().isAssignableFrom(C.class)); //Can I write C c = new C(), Yes. So this is true.
还有另一个区别:
无论X是什么,null的instanceof X都是false
null.getClass()。isAssignableFrom(X)将抛出一个NullPointerException
还有一个区别。 如果要testing的types(Class)是dynamic的,例如作为方法parameter passing,那么instanceof将不会为你剪切。
boolean test(Class clazz) { return (this instanceof clazz); // clazz cannot be resolved to a type. }
但你可以这样做:
boolean test(Class clazz) { return (clazz.isAssignableFrom(this.getClass())); // okidoki }
哎呀,我看到这个答案已经覆盖。 也许这个例子对某人有帮助。
这个线程提供了一些有关isAssignableFrom
与instanceof
不同的isAssignableFrom
,所以我想我会分享一些我自己的东西。
我发现,使用isAssignableFrom
是唯一的(可能不是唯一的,但可能是最简单的)方式来问自己,如果一个类的引用可以采取另一个类的实例,当一个类没有实例进行比较的情况下。
因此,我没有发现使用instanceof
运算符比较赋值是一个好主意,当我所有的是类时,除非我考虑从一个类创build一个实例; 我认为这将是马虎。
考虑以下情况。 假设你想检查typesA是否是objtypes的超类,你可以去
… A.class.isAssignableFrom(obj.getClass())…
要么
… obj instanceof A …
但isAssignableFrom解决scheme要求obj的types在这里可见。 如果情况并非如此(例如,obj的types可能是私有的内部类),那么这个选项就没有了。 但是,解决scheme的实例总是可行的。
instanceof不能用于原始types或genericstypes。 如下面的代码所示:
//Define Class< T > type ... Object e = new Object(); if(e instanceof T) { // Do something. }
错误是:无法对types参数T执行instanceof检查。因为进一步的genericstypes信息将在运行时被删除,所以使用它的erasure Object。
删除运行时引用不会因types删除而编译。 但是,下面的代码将编译:
if( type.isAssignableFrom(e.getClass())){ // Do something. }
isAssignableFrom(A, B) = if (A == B) return true else if (B == java.lang.Object) return false else return isAssignableFrom(A, getSuperClass(B))
上面的伪代码是一个定义,如果types/类A的引用是可以从types/类B的引用中分配的。这是一个recursion定义。 对于某些人来说,这可能是有帮助的,对于其他人来说,这可能会让人困惑 我添加它,以防有人认为它有用。 这只是一个试图捕捉我的理解,这不是官方的定义。 它被用在某个Java VM实现中,并且适用于许多示例程序,所以虽然我不能保证它捕获isAssignableFrom的所有方面,但它并不完全closures。
performance“2”(与JMH)谈话:
class A{} class B extends A{} public class InstanceOfTest { public static final Object a = new A(); public static final Object b = new B(); @Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) public boolean testInstanceOf() { return b instanceof A; } @Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) public boolean testIsInstance() { return A.class.isInstance(b); } @Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) public boolean testIsAssignableFrom() { return A.class.isAssignableFrom(b.getClass()); } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(InstanceOfTest.class.getSimpleName()) .warmupIterations(5) .measurementIterations(5) .forks(1) .build(); new Runner(opt).run(); } }
它给:
Benchmark Mode Cnt Score Error Units InstanceOfTest.testInstanceOf avgt 5 1,972 ? 0,002 ns/op InstanceOfTest.testIsAssignableFrom avgt 5 1,991 ? 0,004 ns/op InstanceOfTest.testIsInstance avgt 5 1,972 ? 0,003 ns/op
所以我们可以得出结论: instanceof与isInstance()和isAssignableFrom()相距不远(+ 0.9%的执行时间)。 所以不pipe你select什么东西
我们在我们团队中做的一些testing显示, A.class.isAssignableFrom(B.getClass())
比B instanceof A
更快。 这可以是非常有用的,如果你需要检查大量的元素。