【问题标题】:What is the difference between instanceof and Class.isAssignableFrom(...)?instanceof 和 Class.isAssignableFrom(...) 有什么区别?
【发布时间】:2010-10-04 13:25:41
【问题描述】:

以下哪个更好?

a instanceof B

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

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

【问题讨论】:

  • 据记载,isInstance() 是检查对象是否可以转换为类类型最方便的方法(更多详细信息,请参见:tshikatshikaaa.blogspot.nl/2012/07/…
  • 反驳@JérômeVerstrynge 的建议:instanceof 构造是检查变量是否可以静态转换为某种类型的首选方法,因为在类型不兼容的情况。 java.lang.Class 中的 isInstance() 方法工作方式不同,仅在运行时进行类型检查,因此在开发早期不会检测到不兼容的类型,可能导致死代码。 isInstance() 方法只应在不能使用 instanceof 运算符的动态情况下使用。

标签: java instanceof reflection


【解决方案1】:

使用instanceof时,需要在编译时知道B的类。使用isAssignableFrom() 时,它可以是动态的,并且在运行时会发生变化。

【讨论】:

  • 我不明白 - 请详细说明为什么我们不能写a instanceof Bref.getClass()。这怎么可能是没有解释(或缺乏解释)的公认答案?
  • 语法是a instanceof Bref 而不是a instanceof Bref.class。 instanceof 运算符的第二个参数是类名,而不是解析为类对象实例的表达式。
  • 是的,“动态”不言而喻 :) 除了性能,这是真正的区别。
  • @EliranMalka 也许您可以拥有一个在运行时生成的类。像代理对象。
  • 如果你有一个对象a,并且你知道类型B在编译类型,使用a instanceof B。如果您有a,但您不知道B 的类型,但您有一个对象b,请使用b.getClass().isInstance(a)。如果您有a,但您没有对象而是Class<?> someBClass,请使用someBClass.isInstance(a)。如果您有两个 Class<?> 但没有实际实例化类型的对象,则一个条件是您使用 someBClass.isAssignableFrom(someAClass) 时。
【解决方案2】:

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)

【讨论】:

  • 我看不出将 instanceof/isAssignableFrom 与原始类型一起使用的意义。
【解决方案3】:

就性能而言:

TL;DR

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

按表现排序:

  1. isInstance
  2. instanceof (+ 0.5%)
  3. isAssignableFrom (+ 2.7%)

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

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

基准

致谢:根据@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 直接扩展 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,代码在某些时候得到了优化,我们得到:

  • 实例:6ms
  • isInstance:12ms
  • isAssignableFrom : 15 毫秒

注意

最初这篇文章是在原始 JAVA 中使用 for 循环进行自己的基准测试,结果不可靠,因为某些优化(如 Just In Time)可以消除循环。所以它主要是测量 JIT 编译器优化循环需要多长时间:有关更多详细信息,请参阅 Performance test independent of the number of iterations

相关问题

【讨论】:

  • 是的,instanceof 是一个字节码,它使用与checkcast 基本相同的逻辑(转换后的字节码)。无论 JITC 优化程度如何,它本质上都会比其他选项更快。
  • 这是有道理的,因为isAssignableFrom() 是动态的。
  • 是的,JMH 的结果完全不同(所有人的速度相同)。
  • 嗨,很好的基准测试,刚刚遇到了 isAssignableFrom 被调用数千次的情况,更改为 instanceof 确实有所作为。这个回复值得在某个地方发表一篇博文...... ;)
【解决方案4】:

更直接的等价于a instanceof B

B.class.isInstance(a)

这在a 也是null 时有效(返回false)。

【讨论】:

  • 很酷,但这不能回答问题,应该是评论。
【解决方案5】:

除了上面提到的基本区别外,class中的instanceof运算符和isAssignableFrom方法还有一个核心的细微差别。

instanceof 读为“这是(左侧)这个实例或this 的任何子类(右侧)”,并将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.

【讨论】:

  • b instanceof A 等同于 A.class.isAssignableFrom(b.getClass()) (正如 OP 所注意到的)。你的例子是正确的,但无关紧要。
  • 因为如果Y 是抽象的或没有公共默认构造函数,new Y() 可能不合法,你可以说X x = (Y)null 是合法的当且仅当x.getClass().isAssignableFrom(Y.class) 为真。
  • 为什么在这个例子中使用 'b.getClass().isAssignableFrom(A.class)'?我猜例子应该是反向的 A.class.isAssignableFrom(b.getClass())。
【解决方案6】:

还有一个区别:

null instanceof X 是 false 不管 X 是什么

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

【讨论】:

  • -1,不正确:null instanceof X(其中 X 是编译时已知的某个类)将始终返回 false
  • @Caspar 虽然你是对的,但基本思想是一个好点。我编辑了帖子,使其正确。
  • 这很有帮助,边缘情况总是很重要:)。
  • 要等价于第一行,第二行应该是X.class.isAssignableFrom(null.getClass()) 不是吗?但是,是的,在空引用上调用 getClass() 将导致 NPE。
  • 这个答案没有抓住重点—— null 取消引用是不相关的,因为失败发生在操作之外(在使用这样的引用之前,您总是需要检查 null)。一般来说getClass()首先不应该和isAssignableFrom一起使用——这个操作是针对没有对象的情况。如果您有对象引用a,请使用a instanceof SomeClass(如果您知道类型SomeClass)或someObject.getClass().isInstance(a)(如果您知道someObject) 的类型。
【解决方案7】:

还有另一个区别。如果要测试的类型(类)是动态的,例如作为方法参数传递,那么 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 work w/ classes,Class.isInstance 是 'instanceof' 的模拟
  • 将@bestsss 的正确注释放入具体代码中:因为您有一个对象(this),所以clazz.isInstance(this) 在您的示例中会更好。
【解决方案8】:

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

我发现使用isAssignableFrom 是唯一(可能不是唯一,但可能是最简单的)方法来询问一个人的一个类的引用是否可以采用另一个类的实例,当一个类没有两个类的实例时进行比较。

因此,当我只有类时,我发现使用 instanceof 运算符比较可分配性不是一个好主意,除非我打算从其中一个类创建实例;我以为这会很草率。

【讨论】:

    【解决方案9】:

    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】:

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

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

      ... obj instanceof A ...

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

      【讨论】:

      • 这不是真的。请看“亚当·罗森菲尔德”评论stackoverflow.com/questions/496928/…
      • 您能否详细说明“这不是真的”?您提到的评论与我帖子中的场景无关。我确实有一些测试代码来支持我的解释。
      • 如果你有一个对任何类型的对象实例(在这个例子中是obj)的非空引用,那么你可以在它上面调用公共的getClass()方法获取实现类的反射元数据。即使实现类类型在编译时在该位置不合法可见,也是如此。在运行时没问题,因为为了让您持有 obj 引用,一些最终确实对类具有合法访问权限的代码路径创建了一个并将其提供(泄露?)给您。
      • 这个答案想要列出obj instanceof A 的模拟。那将是A.class.isInstance(obj)。如果你有两个类和零个对象,你应该只使用isAssignableFrom
      【解决方案11】:
      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 的所有方面,但它并没有完全关闭。

      【讨论】:

      • 请解释这段代码的作用以及它如何回答问题。
      【解决方案12】:

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

      【讨论】:

        【解决方案13】:

        举一些例子来展示它的实际效果如何......

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

        【讨论】:

          【解决方案14】:

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

          【讨论】:

          • 嗯,如果你在instanceof有瓶颈,我相信你有严重的设计问题......
          • JBE 的回答提出了一个与您的假设不同的假设。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-04-05
          • 2011-01-28
          • 1970-01-01
          • 2013-08-06
          • 2010-10-28
          • 2010-10-02
          • 2011-12-12
          相关资源
          最近更新 更多