下面哪个比较好?

a instanceof B

or

B.class.isAssignableFrom(a.getClass())

我所知道的唯一区别是,当'a'为空时,第一个返回false,而第二个抛出异常。除此之外,它们总是给出相同的结果吗?


当前回答

在使用instanceof时,您需要在编译时知道B的类。当使用isAssignableFrom()时,它可以是动态的,并在运行时更改。

其他回答

还有一个不同之处。如果要测试的类型(Class)是动态的,例如,作为方法参数传递,那么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
}

哎呀,这个答案已经讲过了。也许这个例子对某人有帮助。

考虑以下情况。假设你想检查类型A是否是obj类型的超类,你也可以这样做

... A.class.isAssignableFrom(obj.getClass()) ...

OR

... A . obj instanceof ...

但是isAssignableFrom解决方案要求obj的类型在这里是可见的。如果不是这样(例如,obj的类型可能是一个私有内部类),则该选项无效。然而,instanceof solution总是可以工作的。

isAssignableFrom(A, B) =

if (A == B) return true
else if (B == java.lang.Object) return false
else return isAssignableFrom(A, getSuperClass(B))

The pseudo code above is a definition of, if references of type/class A is assignable from references of type/class B. It is a recursive definition. To some it may be helpful, for others it may be confusing. I add it in case somebody should find it useful. This is just an attempt to capture my understanding, it is not the official definition. It is used in a certain Java VM implementation and works for many example programs, so while I cannot guarentee that it captures all aspects of isAssignableFrom, it is not completely off.

Instanceof也不能用于基本类型或泛型类型。如下代码所示:

//Define Class< T > type ... 

Object e = new Object();

if(e instanceof T) {
  // Do something.
}

错误是:不能对类型参数t执行instanceof检查,而是使用它的erasure对象,因为进一步的泛型类型信息将在运行时被擦除。

由于删除运行时引用的类型擦除而无法编译。然而,下面的代码将编译:

if( type.isAssignableFrom(e.getClass())){
  // Do something.
}

谈论业绩:

博士TL;

请使用性能相似的isInstance或instanceof。isAssignableFrom稍微慢一点。

按性能排序:

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);

测量每个操作符使用了多少字节码指令,我们可以期望instanceof和isInstance比isAssignableFrom快。然而,实际性能不是由字节码决定的,而是由机器代码决定的(这是依赖于平台的)。让我们为每个操作符做一个微基准测试。

基准

根据@aleksandr-dubinsky的建议,并感谢@yura提供的基本代码,这里是一个JMH基准测试(参见此调优指南):

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

警告

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...

给你一个例子,看下面的循环:

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,代码在某些时候被优化了,我们得到:

运算符:6女士 女士isInstance: 12 isAssignableFrom: 15毫秒

Note

最初这篇文章是在原始JAVA中使用for循环进行自己的基准测试,这给出了不可靠的结果,因为一些优化如Just in Time可以消除循环。因此主要是衡量JIT编译器优化循环所花费的时间:有关更多细节,请参阅与迭代次数无关的性能测试

相关问题

instanceof操作符是否产生大量开销?为什么? instanceof是如何在JAVA中实现的? 在Java中使用instanceof对性能的影响