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

使用具有相似性能的isInstanceinstanceofisAssignableFrom稍微慢一些。

按性能sorting:

  1. isInstance
  2. instanceof (+ 0.5%)
  3. 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字节码指令,我们可以预期instanceofisInstanceisAssignableFrom快。 然而,实际的性能不是由字节码来决定的,而是由机器码决定(取决于平台)。 让我们为每个运营商做一个微观基准。

基准

学分:正如@ 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 } 

哎呀,我看到这个答案已经覆盖。 也许这个例子对某人有帮助。

这个线程提供了一些有关isAssignableFrominstanceof不同的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 

所以我们可以得出结论: instanceofisInstance()isAssignableFrom()相距不远(+ 0.9%的执行时间)。 所以不pipe你select什么东西

我们在我们团队中做的一些testing显示, A.class.isAssignableFrom(B.getClass())B instanceof A更快。 这可以是非常有用的,如果你需要检查大量的元素。