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


457

以下哪个更好?

a instanceof B

要么

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

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


17
对于记录,isInstance()是检查对象是否可以被铸造成一个类类型的最方便的方法(更多详情,请参阅:tshikatshikaaa.blogspot.nl/2012/07/...
杰罗姆Verstrynge

Answers:


497

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


12
我不明白-请详细说明为什么我们不能写a instanceof Bref.getClass()。这么少的解释(或缺乏解释)怎么能成为公认的答案?
Eliran Malka

65
语法a instanceof Bref不是a instanceof Bref.class。instanceof运算符的第二个参数是类名,而不是解析为类对象实例的表达式。
布兰登·布鲁姆

2
是的,“动态”不言而喻:)除了性能,这是真正的区别。
彼得

2
@EliranMalka也许您可以拥有一个在运行时生成的类。像代理对象。
瓦格纳土屋

因此,在中B.class.isAssignableFrom(a.getClass()),B是已知的,并且a instanceof B更好。对?
Florian F


116

谈论表现:

TL; DR

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

按效果排序:

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

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

基准

致谢:根据@ 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

警告

  • 基准测试取决于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,代码在某些时候得到了优化,我们得到了:

  • instanceof:6毫秒
  • isInstance:12毫秒
  • isAssignableFrom:15ms

注意

最初,该文章使用原始JAVA中的for循环进行自己的基准测试,结果不可靠,因为某些优化(如Just In Time)可以消除循环。因此,主要是测量JIT编译器花费多长时间来优化循环:有关更多详细信息,请参见性能测试,该测试与迭代次数无关

相关问题


6
instanceof是的,是一种字节码,其使用的逻辑基本上与checkcast(强制转换后面的字节码)逻辑相同。无论JITC优化的程度如何,它本质上都会比其他选项更快。
热门点击2013年

1
这是有道理的,就像isAssignableFrom()是动态的。
Matthieu

是的,JMH的结果完全不同(所有速度都相同)。
Yura

嗨,好的基准测试,刚好遇到了isAssignableFrom被调用过数千次的情况,更改为instanceof确实起了很大作用。此回复将值得在某个地方发布博客; ...)
Martin

33

更直接等同于a instanceof BIS

B.class.isInstance(a)

该作品(返回false)时anull也。


很酷,但这不能回答问题,应该发表评论。
Madbreaks

23

除了上述基本差异外,Class中的instanceof运算符和isAssignableFrom方法之间还有一个核心的细微差异。

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

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

3
b instanceof A等同于A.class.isAssignableFrom(b.getClass())(如OP所注意到的)。您的示例是正确的,但无关紧要。
卡鲁2012年

由于new Y()如果Y是抽象的或没有公共默认构造函数,可能不合法,因此可以X x = (Y)null且仅当x.getClass().isAssignableFrom(Y.class)为true 时才说是合法的。
Earth Engine

2
为什么在此示例中为“ b.getClass()。isAssignableFrom(A.class)”?我猜例子应该是反向的A.class.isAssignableFrom(b.getClass())。
loshad vtapkah 2014年

14

还有另一个区别:

X的null实例false无论X是什么

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


4
-1,不正确:(null instanceof X其中X是在编译时已知的某个类)将始终返回false
卡斯珀

4
@Caspar没错,但基本思路很不错。我编辑了帖子,以确保它是正确的。
erickson

1
这很有用,边缘情况总是很重要的:)。
万亿

要等同于第一行,第二行应该X.class.isAssignableFrom(null.getClass())不是吗?但是,是的,调用getClass()空引用将导致NPE。
威廉·普赖斯

这个答案错过了重点-空取消引用是不相关的,因为失败发生在操作之外(在使用这样的引用之前,您始终需要检查是否为空)。通常getClass(),一开始不应该使用isAssignableFrom-该操作是针对没有对象的情况。如果你有对象引用a,使用a instanceof SomeClass(如果你知道类型SomeClass),或someObject.getClass().isInstance(a)(如果你知道的类型someObject)。
AndrewF

12

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

糟糕,我已经看到此答案了。也许这个例子对某人有帮助。


3
实际上没有答案是真正正确的isAssignableFromwork w / classes,Class.isInstance是'instanceof'
bestsss

将@bestssss的正确注释放入具体代码中:因为您有一个对象(this),clazz.isInstance(this)所以在您的示例中会更好。
AndrewF

7

该线程为我提供了instanceof与的区别的一些见解isAssignableFrom,因此我认为自己应该分享一些自己的东西。

我发现,isAssignableFrom当一个人的引用没有一个类的实例可以进行比较时,曾经是询问一个人自身的唯一(可能不是唯一的,但可能是最简单的)方法。

因此,instanceof除非我打算从其中一个类中创建一个实例,否则我并没有发现使用运算符比较可分配性是个好主意。我以为这会马虎。


5

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

//Define Class< T > type ... 

Object e = new Object();

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

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

由于类型删除导致无法编译,因此删除了运行时引用。但是,下面的代码将编译:

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

4

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

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

要么

... obj instanceof A ...

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


2
那是不对的。请参阅“亚当罗森菲尔德”的评论stackoverflow.com/questions/496928/...
马克西姆Veksler

1
您能否详细说明“那不是真的”?您引用的评论与我的帖子中的场景无关。我确实有一些测试代码来支持我的解释。
代数

如果您具有任何类型的对象实例的非空引用(obj在此示例中),则可以在其上调用public 方法以获取实现类的反射元数据。即使在编译时该实现类类型在该位置在法律上不可见,也是如此。这在运行时还可以,因为对于您来说,保留引用是一些代码路径,这些代码路径最终确实可以合法访问该类,因此创建了该类并将其提供(泄漏了)。getClass()obj
威廉·普赖斯

0
isAssignableFrom(A, B) =

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

如果可以从类型/类B的引用中分配类型/类A的引用,则上面的伪代码是一个定义。它是一个递归定义。对于某些人来说可能是有帮助的,对于其他人来说则可能令人困惑。我添加它是为了防止有人觉得它有用。这仅是为了获取我的理解,并非官方定义。它用于某个Java VM实现中,并且可用于许多示例程序,因此尽管我无法保证它捕获了isAssignableFrom的所有方面,但并不能完全解决。


2
请解释该代码的作用以及如何回答该问题。
基金莫妮卡的诉讼

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%)。所以无论您选择什么都没有真正的区别


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

-2

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


13
嗯,如果您遇到瓶颈instanceof,我相信您有严重的设计问题……
sleske 2011年

1
JBE的答案提出了与您的假设不同的假设。
Alastor Moody 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.