instanceof和Class.isAssignableFrom(…)的区别是什么?

下面哪个比较好?

a instanceof B

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

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

277934 次浏览

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时,你需要在编译时知道B的类。当使用isAssignableFrom()时,它可以是动态的,并在运行时变化。

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

< p >… A.class.isAssignableFrom (obj.getClass ()) …< / p >

< p >… A . obj instanceof …< / p >

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

我们团队做的一些测试表明,A.class.isAssignableFrom(B.getClass())B instanceof A工作得更快。如果需要对大量元素进行检查,这将非常有用。

除了上面提到的基本区别之外,在类中的instanceof操作符和isAssignableFrom方法之间还有一个核心的微妙区别。

instanceof读为“这(左边部分)是这个或这个(右边部分)的任何子类的实例”,并将x.getClass().isAssignableFrom(Y.class)读为“我可以写X x = new Y()”。换句话说,instanceof操作符检查左对象是否相同或右对象的子类,而isAssignableFrom检查是否可以将形参类(from)的object赋值给调用方法的类的引用 注意,这两种方法都考虑实际实例而不是引用类型

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

还有另一个区别:

不管X是什么,null instanceof X都是false

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

还有一个不同之处。如果要测试的类型(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 instanceof B更直接等价的是

B.class.isInstance(a)

anull时,此方法也有效(返回false)。

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

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

因此,当我只有类时,我不认为使用instanceof操作符来比较可赋值性是个好主意,除非我打算从其中一个类创建一个实例;我觉得这样会很草率。

谈论业绩:

博士TL;

使用性能相似的isInstance运算符isAssignableFrom略慢。

按性能排序:

  1. isInstance
  2. __abc0 (+ 0.5%)
  3. __abc0 (+ 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);

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

基准

出处:根据@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, X扩展Y, Y扩展Z, 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,代码在某些时候被优化了,我们得到:

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

请注意

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

相关问题

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.
}
isAssignableFrom(A, B) =


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

上面的伪代码是一个定义,if类型/类a的引用可以从类型/类b的引用赋值。这是一个递归定义。对一些人来说,这可能是有帮助的,对另一些人来说,这可能是令人困惑的。我加上它,以防有人发现它有用。这只是我自己的理解,并不是官方定义。它被用于特定的Java VM实现中,并适用于许多示例程序,因此虽然我不能保证它捕获isAssignableFrom的所有方面,但它并不是完全关闭的。

在性能“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

因此,我们可以得出结论:运算符isInstance ()一样快,isAssignableFrom ()不远(+0.9%的执行时间)。所以无论你选择什么都没有真正的区别

用一些例子来证明它怎么样?

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

这完全取决于代码中此时可用的内容。如果你正在处理一个实际的对象,我不建议使用isAssignableFrom -有一个更好的选择。下面是一份根据你所拥有的产品进行排名的推荐列表:

  • 如果你有一个对象a,并且你在编译时知道类型B:

    a instanceof B

  • 如果你有一个对象a并且你不知道类型B,但是你有一个对象b:

    b.getClass().isInstance(a)

  • 如果你有一个对象a,并且在编译时没有类型或实例化对象,但你有一个类对象Class<?> someBClass:

    someBClass.isInstance(a)

  • 如果你没有实例化的对象,但是你有两个Class<?>对象:

    someBClass.isAssignableFrom(someAClass)

假设你从列表的顶部开始向下工作,每一个都是实现你的目标的最简单的方法,并且根据我自己的研究我相信,你也会得到最好的表现。