首页 文章

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

提问于
浏览
415

以下哪项更好?

a instanceof B

要么

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

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

13 回答

  • 33

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

  • 0

    在性能方面谈论:

    TL;DR

    使用具有相似性能的 isInstanceinstanceof . isAssignableFrom 稍慢 .

    按性能排序:

    • isInstance

    • instanceof (0.5%)

    • isAssignableFrom (2.7%)

    基于JAVA 8 Windows x64上2000次迭代的基准测试,具有20次预热迭代 .

    In theory

    使用类似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);
    

    测量每个运算符使用多少字节码指令,我们可以预期 instanceofisInstanceisAssignableFrom 更快 . 但是,实际性能不是由字节码决定的,而是由机器代码决定的(取决于平台) . 让我们为每个运营商做一个微基准测试 .

    The benchmark

    信用:正如@ 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
    

    Warning

    • 该基准测试依赖于JVM和平台 . 由于每个操作之间没有显着差异,因此可能在不同的JAVA版本和/或Solaris,Mac或Linux等平台上获得不同的结果(可能是不同的顺序!) .

    • 基准比较直接"B extends A"时"is B an instance of A"的性能 . 如果类层次结构更深且更复杂(如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

    Note

    最初这篇文章是在原始JAVA中使用for循环进行自己的基准测试,由于像Just In Time这样的优化可以消除循环,因此产生了不可靠的结果 . 因此,它主要测量JIT编译器优化循环所需的时间:有关详细信息,请参阅Performance test independent of the number of iterations

    Related questions

  • 22

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

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

    要么

    ... obj instanceof A ...

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

  • 3

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

    还有另一个不同之处:

    无论X是什么,null instanceof X都是 false

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

  • 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的所有方面,但它并没有完全关闭 .

  • 4

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

  • 101

    在性能方面谈论“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%执行时间) . 所以没有真正的区别无论你选择什么

  • -2

    instanceof也不能与基本类型或泛型类型一起使用 . 如下面的代码所示:

    //Define Class< T > type ... 
    
    Object e = new Object();
    
    if(e instanceof T) {
      // Do something.
    }
    

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

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

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

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

  • 451

    更直接等同于 a instanceof B

    B.class.isInstance(a)
    

    a 也是 null 时,这可以工作(返回false) .

  • 10

    除了上面提到的基本差异之外,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.
    
  • 200

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

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

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

相关问题