instanceof和Class.isAssignableFrom(...)有什么区别?

时间:2009-01-30 19:44:24

标签: java instanceof reflection

以下哪项更好?

a instanceof B

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

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

14 个答案:

答案 0 :(得分:465)

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

答案 1 :(得分:201)

instanceof只能用于引用类型,而不能用于基本类型。 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)

答案 2 :(得分:106)

谈论绩效:

<强> TL; DR

使用具有类似效果的 isInstance instanceof isAssignableFrom 略慢。

按表现排序:

  1. isInstance
  2. instanceof (+ 0.5%)
  3. isAssignableFrom (+ 2.7%)
  4. 基于JAVA 8 Windows x64上2000次迭代的基准测试,有20次预热迭代。

    理论上

    使用像bytecode viewer这样的软,我们可以将每个运算符转换为字节码。

    在上下文中:

    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基准(见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();
        }
    }
    

    给出以下结果(得分是时间单位中的一些操作,因此得分越高越好):

    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的实例”的性能。如果类层次结构更深且更复杂(如B扩展X扩展Y,扩展Z扩展A),结果可能会有所不同。
    • 通常建议首先选择一个运算符(最方便)编写代码,然后分析代码以检查是否存在性能瓶颈。也许这个运算符在代码的上下文中可以忽略不计,或者可能......
    • 与上一点相关,代码上下文中的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 循环进行自己的基准测试,由于Just In Time等一些优化可以消除循环,因此产生了不可靠的结果。因此,它主要测量JIT编译器优化循环所需的时间:有关更多详细信息,请参阅Performance test independent of the number of iterations

    相关问题

答案 3 :(得分:33)

更直接等同于a instanceof B

B.class.isInstance(a)

a null为{{1}}时,此方法有效(返回false)。

答案 4 :(得分:22)

除了上面提到的基本差异之外,instanceof运算符和Class中的isAssignableFrom方法之间存在核心细微差别。

instanceof读为“这是(左侧部分)此实例或其任何子类(右侧部分)的实例”并将x.getClass().isAssignableFrom(Y.class)读为“我可以写X x = new Y() ”。换句话说,instanceof运算符检查左对象是否是右类的子类,而isAssignableFrom检查我们是否可以将参数类(from)的对象分配给调用该方法的类的引用。
请注意,这两个都认为实际的实例不是引用类型。

考虑3个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.

答案 5 :(得分:15)

还有另一个不同之处:

无论X是什么

null instanceof X都是false

null.getClass()。isAssignableFrom(X)将抛出NullPointerException

答案 6 :(得分:11)

还有另一个不同之处。如果要测试的类型(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
}

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

答案 7 :(得分:7)

这个帖子让我对instanceofisAssignableFrom的不同之处有所了解,所以我想我会分享一些属于自己的东西。

我发现使用isAssignableFrom是唯一的(可能不是唯一的,但可能是最简单的)方式来询问自己,如果一个类的引用可以接受另一个类的实例,当一个类具有实例时两个人都没有做比较。

因此,我没有发现使用instanceof运算符来比较赋值是一个好主意,当我所有的都是类时,除非我考虑从其中一个类创建一个实例;我以为这会马虎。

答案 8 :(得分:4)

考虑以下情况。假设你要检查类型A是否是obj类型的超类,你可以去

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

OR

... obj instanceof A. ...

但isAssignableFrom解决方案要求obj的类型在这里可见。如果不是这种情况(例如,obj的类型可能是私有内部类),则该选项不存在。但是,解决方案的实例始终有效。

答案 9 :(得分:3)

instanceof也不能与原始类型或泛型类型一起使用。如下面的代码所示:

//Define Class< T > type ... 

Object e = new Object();

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

错误是:无法对类型参数T执行instanceof检查。请使用它的擦除对象,因为在运行时将删除其他泛型类型信息。

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

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

答案 10 :(得分:0)

isAssignableFrom(A, B) =

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

如果类型/类A的引用可以从类型/类B的引用分配,则上面的伪代码是一个定义。它是一个递归定义。对某些人来说,这可能会有所帮助,对于其我添加它以防有人应该发现它有用。这只是为了捕捉我的理解,它不是官方的定义。它用于某个Java VM实现并适用于许多示例程序,因此虽然我不能保证它捕获了isAssignableFrom的所有方面,但它并没有完全关闭。

答案 11 :(得分:0)

谈论表现“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%执行时间) )。无论你选择什么都没有真正的区别

答案 12 :(得分:0)

如何在实际操作中展示一些示例...

@Test
public void isInstanceOf() {
    Exception anEx1 = new Exception("ex");
    Exception anEx2 = new RuntimeException("ex");
    RuntimeException anEx3 = new RuntimeException("ex");

    //Base case, handles inheritance
    Assert.assertTrue(anEx1 instanceof Exception);
    Assert.assertTrue(anEx2 instanceof Exception);
    Assert.assertTrue(anEx3 instanceof Exception);

    //Other cases
    Assert.assertFalse(anEx1 instanceof RuntimeException);
    Assert.assertTrue(anEx2 instanceof RuntimeException);
    Assert.assertTrue(anEx3 instanceof RuntimeException);
}

@Test
public void isAssignableFrom() {
    Exception anEx1 = new Exception("ex");
    Exception anEx2 = new RuntimeException("ex");
    RuntimeException anEx3 = new RuntimeException("ex");

    //Correct usage = The base class goes first
    Assert.assertTrue(Exception.class.isAssignableFrom(anEx1.getClass()));
    Assert.assertTrue(Exception.class.isAssignableFrom(anEx2.getClass()));
    Assert.assertTrue(Exception.class.isAssignableFrom(anEx3.getClass()));

    //Incorrect usage = Method parameter is used in the wrong order
    Assert.assertTrue(anEx1.getClass().isAssignableFrom(Exception.class));
    Assert.assertFalse(anEx2.getClass().isAssignableFrom(Exception.class));
    Assert.assertFalse(anEx3.getClass().isAssignableFrom(Exception.class));
}

答案 13 :(得分:-2)

我们在团队中进行的一些测试表明,A.class.isAssignableFrom(B.getClass())的工作速度比B instanceof A快。如果您需要在大量元素上进行检查,这可能非常有用。