instanceof和Class.isAssignableFrom的区别

2023-02-12,,

1. Class.isAssignableFrom

偶然看见同事写的一段代码是这样的

   if( AfterRender.class.isAssignableFrom( assembly.getClass() ) ){
afterRenders.add( ( AfterRender )assembly );
}

其中用了Class类的isAssignableFrom方法,以前从来没见过这个方法,于是百度了一下, 是这样说的

A.isAssignableFrom(B) 确定一个类(B)是不是继承来自于另一个父类(A),一个接口(A)是不是实现了另外一个接口(B)

也就是说,不管是类继承还是接口实现,是来判断A是不是B的父,B是不是A的子,父.isAssignableFrom(子) == true

2. instanceof

看了Class.isAssignableFrom,想到了instanceof,这个大家应该都比较熟悉

instanceof 是 Java 中的一个双目运算符

obj instanceof Class

    声明一个 class 类的对象,判断 obj 是否为 class 类的实例对象
    声明一个 class 接口实现类的对象 obj,判断 obj 是否为 class 接口实现类的实例对象

乍一看,这和上面Class.isAssignableFrom不是完全一样吗?

3. 对比

3.1 参数类型和位置

A.isAssignableFrom(B)中的A和B都是Java中的Class对象;父在前,子在后
obj instanceof Class中obj表示的是实例,而Class准确的讲是一个类或接口名,并不是一个Class对象;子在前,父在后

e instanceof List.class // 这种写法是错的,所以不可能动态的把别的Class放在instanceof后面

e instanceof List // 这个写法是对的

3.2 编译与运行时

When using instanceof, you need to know the class of B at compile time. When using isAssignableFrom() it can be dynamic and change during runtime

obj instanceof Class中的Class必须在编译的时候知道类型,
而isAssignableFrom可以在运行时修改

3.3 基础类型

instanceof can only be used with reference types, not primitive types. isAssignableFrom() can be used with any class objects

isAssignableFrom可以用于基础类型

a instanceof int  // syntax error
3 instanceof Foo // syntax error
int.class.isAssignableFrom(int.class) // true

不过因为基础类型不能继承,所以这个看起来并没有什么用处

3.4 性能

通过简单的测试,性能由好到坏依次是:

    instanceof
    isInstance
    isAssignableFrom

其中的isInstance后面会进行单独对比

底层字节码对比, instanceof相当于关键字,而invokevirtual表示调用了方法,所以一个是静态,另外两个是动态,静态一般是比动态的性能要好

// JAVA
b instanceof A; // Bytecode
getstatic foo/Benchmark.b:java.lang.Object
instanceof foo/A // JAVA
A.class.isInstance(b); // Bytecode
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()); // Bytecode
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);

性能测试代码:

public class InstanceBenchmark {
static B c = new C(); static boolean execute() {
return B.class.isAssignableFrom(c.getClass()); // 36ms
// return B.class.isInstance(c); // 17ms
// return c instanceof B; // 9ms
} public static void main(String[] args) {
for (int i = 0; i < 100; ++i)
execute(); int count = 1000000;
final long start = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
execute();
}
final long elapsed = System.currentTimeMillis() - start;
System.out.println(elapsed);
}
} class B {
} class C extends B {
} // Warmup the code

3.5 null对象

        boolean a = null instanceof Point; // false
boolean b = Point.class.isInstance(null); // false
boolean c = Point.class.isAssignableFrom(null); // NullPointerException

根据上述代码,可以得知instanceof和isInstance的子可以是null, isAssignableFrom的子不是null, 他们的父都不可以是null

4. Class.isInstance

上面的对比中加入了isInstance这个方法,这个方法可以看作和Class.isAssignableFrom一样,除了下述区别

    isInstance的参数是对象,isAssignableFrom的参数是类
    isInstance的参数可以是null, isAssignableFrom参数不可为null
    isInstance的性能比isAssignableFrom略好

5. 总结

   if( AfterRender.class.isAssignableFrom( assembly.getClass() ) ){
afterRenders.add( ( AfterRender )assembly );
}

再回头看一下之前同事写的代码,这里必须需要用isAssignableFrom吗?

不是,因为子是assembly,是个对象,所以可以用isInstance或者instanceof

又因为父是AfterRender,一个固定的类,所以从性能角度讲,用instanceof最好

所以再使用的过程中,要结合当时的情况,现在的参数是类还是对象,要判断的父类是静态的还是动态的综合考虑

参考

[1] What is the difference between instanceof and Class.isAssignableFrom(...)?

[2] isAssignableFrom的用法详细解析

[3] Java instanceof关键字详解

instanceof和Class.isAssignableFrom的区别的相关教程结束。

《instanceof和Class.isAssignableFrom的区别.doc》

下载本文的Word格式文档,以方便收藏与打印。