Class.isInstance vs Class.isAssignableFrom and instanceof – Class.isInstance vs Class.isAssignableFrom and instanceof

最后修改: 2020年 7月 14日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

In this quick tutorial, we’re going to take a look at the difference between instanceof, Class.isInstance, and Class.isAssignableFrom. We’ll learn how to use each method and what the differences are between them.

在这个快速教程中,我们将看看instanceofClass.isInstanceClass.isAssignableFrom的区别。我们将学习如何使用每个方法,以及它们之间的区别是什么。

2. Setup

2.设置

Let’s set up an interface and a couple of classes to use while we explore the instanceof, Class.isInstance, and Class.isAssignableFrom functionality.

让我们在探索instanceofClass.isInstanceClass.isAssignableFrom功能时,设置一个接口和几个类来使用。

First, let’s define an interface:

首先,让我们定义一个接口。

public interface Shape {
}

Next, let’s define a class that implements Shape:

接下来,让我们定义一个实现Shape的类。

public class Triangle implements Shape {
}

Now, we’ll create a class that extends Triangle:

现在,我们将创建一个扩展Triangle的类。

public class IsoscelesTriangle extends Triangle {
}

3. instanceof

3.instanceof

The instanceof keyword is a binary operator, and we can use it to verify whether a certain object is an instance of a given type. Therefore, the result of the operation is either true or false. Additionally, the instanceof keyword is the most common and straightforward way to check if an object subtypes another type.

instanceof关键字是一个二进制运算符,我们可以用它来验证某个对象是否是某个特定类型的实例。因此,操作的结果是truefalse。此外,instanceof关键字是检查一个对象是否是另一个类型的子类型的最常见和最直接的方法。

Let’s use our classes with the instanceof operator:

让我们用instanceof操作符来使用我们的类。

Shape shape = new Triangle();
Triangle triangle = new Triangle();
IsoscelesTriangle isoscelesTriangle = new IsoscelesTriangle();
Shape nonspecificShape = null;

assertTrue(shape instanceof Shape);
assertTrue(triangle instanceof Shape);
assertTrue(isoscelesTriangle instanceof Shape);
assertFalse(nonspecificShape instanceof Shape);

assertTrue(shape instanceof Triangle);
assertTrue(triangle instanceof Triangle);
assertTrue(isoscelesTriangle instanceof Triangle);
assertFalse(nonspecificShape instanceof Triangle);

assertFalse(shape instanceof IsoscelesTriangle);
assertFalse(triangle instanceof IsoscelesTriangle);
assertTrue(isoscelesTriangle instanceof IsoscelesTriangle);
assertFalse(nonspecificShape instanceof IsoscelesTriangle);

With the above code snippet, we can see that the right-hand side type is more generic than the left-hand side object. More specifically, the instanceof operator will process null values to false.

通过上面的代码片断,我们可以看到右边的类型比左边的对象更通用。更具体地说,instanceof操作符会将null值处理为false

4. Class.isInstance

4.Class.isInstance

The isInstance method on the Class class is equivalent to the instanceof operator. The isInstance method was introduced in Java 1.1 because it can be used dynamically. Generally, this method will return true if the argument isn’t null and can be successfully cast to the reference type without raising a ClassCastException.

isInstance方法在Class类上等同于instanceof操作符。isInstance方法是在Java 1.1中引入的,因为它可以动态地使用。一般来说,如果参数不是null,并且可以成功地转换为引用类型而不引发ClassCastException,那么这个方法将返回true

Let’s look at how we can use the isInstance method with the interface and classes we defined:

让我们看看我们如何用我们定义的接口和类来使用isInstance方法。

Shape shape = new Triangle();
Triangle triangle = new Triangle();
IsoscelesTriangle isoscelesTriangle = new IsoscelesTriangle();
Triangle isoscelesTriangle2 = new IsoscelesTriangle();
Shape nonspecificShape = null;

assertTrue(Shape.class.isInstance(shape));
assertTrue(Shape.class.isInstance(triangle));
assertTrue(Shape.class.isInstance(isoscelesTriangle));
assertTrue(Shape.class.isInstance(isoscelesTriangle2));
assertFalse(Shape.class.isInstance(nonspecificShape));

assertTrue(Triangle.class.isInstance(shape));
assertTrue(Triangle.class.isInstance(triangle));
assertTrue(Triangle.class.isInstance(isoscelesTriangle));
assertTrue(Triangle.class.isInstance(isoscelesTriangle2));

assertFalse(IsoscelesTriangle.class.isInstance(shape));
assertFalse(IsoscelesTriangle.class.isInstance(triangle));
assertTrue(IsoscelesTriangle.class.isInstance(isoscelesTriangle));
assertTrue(IsoscelesTriangle.class.isInstance(isoscelesTriangle2));

As we can see, the right-hand side can be equivalent to or more specific than the left-hand side. In particular, providing null to the isInstance method returns false.

正如我们所看到的,右手边可以等同于或比左手边更具体。特别是,向isInstance方法提供null会返回false

5. Class.isAssignableFrom

5.Class.isAssignableFrom

The Class.isAssignableFrom method will return true if the Class on the left-hand side of the statement is the same as or is a superclass or superinterface of the provided Class parameter.

如果语句左侧的Class与所提供的Class参数的超类或超接口相同,那么Class.isAssignableFrom方法将返回true

Let’s use our classes with the isAssignableFrom method:

让我们使用我们的类与isAssignableFrom方法。

Shape shape = new Triangle();
Triangle triangle = new Triangle();
IsoscelesTriangle isoscelesTriangle = new IsoscelesTriangle();
Triangle isoscelesTriangle2 = new IsoscelesTriangle();

assertFalse(shape.getClass().isAssignableFrom(Shape.class));
assertTrue(shape.getClass().isAssignableFrom(shape.getClass()));
assertTrue(shape.getClass().isAssignableFrom(triangle.getClass()));
assertTrue(shape.getClass().isAssignableFrom(isoscelesTriangle.getClass()));
assertTrue(shape.getClass().isAssignableFrom(isoscelesTriangle2.getClass()));

assertFalse(triangle.getClass().isAssignableFrom(Shape.class));
assertTrue(triangle.getClass().isAssignableFrom(shape.getClass()));
assertTrue(triangle.getClass().isAssignableFrom(triangle.getClass()));
assertTrue(triangle.getClass().isAssignableFrom(isoscelesTriangle.getClass()));
assertTrue(triangle.getClass().isAssignableFrom(isoscelesTriangle2.getClass()));

assertFalse(isoscelesTriangle.getClass().isAssignableFrom(Shape.class));
assertFalse(isoscelesTriangle.getClass().isAssignableFrom(shape.getClass()));
assertFalse(isoscelesTriangle.getClass().isAssignableFrom(triangle.getClass()));
assertTrue(isoscelesTriangle.getClass().isAssignableFrom(isoscelesTriangle.getClass()));
assertTrue(isoscelesTriangle.getClass().isAssignableFrom(isoscelesTriangle2.getClass()));

assertFalse(isoscelesTriangle2.getClass().isAssignableFrom(Shape.class));
assertFalse(isoscelesTriangle2.getClass().isAssignableFrom(shape.getClass()));
assertFalse(isoscelesTriangle2.getClass().isAssignableFrom(triangle.getClass()));
assertTrue(isoscelesTriangle2.getClass().isAssignableFrom(isoscelesTriangle.getClass()));
assertTrue(isoscelesTriangle2.getClass().isAssignableFrom(isoscelesTriangle2.getClass()));

As with the isInstance example, we can clearly see that the right-hand side must be either the same or more specific than the left-hand side. We can also see that we’re never able to assign our Shape interface.

isInstance的例子一样,我们可以清楚地看到,右边必须和左边一样,或者比左边更具体。我们还可以看到,我们永远无法指定我们的Shape接口。

6. The Differences

6.差异

Now that we’ve laid out a few detailed examples, let’s go over some of the differences.

现在我们已经摆出了几个详细的例子,让我们来看看其中的一些区别。

6.1. Semantical Differences

6.1.语义上的差异

Superficially, instanceof is a Java Language keyword. In contrast, both isInstance and isAssignableFrom are native methods from Class type.

表面上看,instanceof是一个Java语言的关键字。相反,isInstanceisAssignableFrom都是来自Class类型的本地方法。

Semantically, we use them to verify the different relationships between two programming elements:

在语义上,我们用它们来验证两个编程元素之间的不同关系。

  • Two objects: we can test if the two objects are identical or equal.
  • An object and a type: we can check whether the object is an instance of the type. Obviously, both the instanceof keyword and the isInstance method belong to this category.
  • Two types: we can examine whether one type is compatible with another type, such as the isAssignableFrom method.

6.2. Usage Corner Case Differences

6.2.使用角落案例的差异

Firstly, they differ with a null value:

首先,它们以null值不同。

assertFalse(null instanceof Shape);
assertFalse(Shape.class.isInstance(null));
assertFalse(Shape.class.isAssignableFrom(null)); // NullPointerException

From the code snippet above, both instanceof and isInstance return false; however, the isAssignableFrom method throws NullPointerException.

从上面的代码片段来看,instanceofisInstance都返回false;但是,isAssignableFrom方法抛出NullPointerException

Secondly, they differ with primitive types:

第二,它们与原始类型不同。

assertFalse(10 instanceof int); // illegal
assertFalse(int.class.isInstance(10));
assertTrue(Integer.class.isInstance(10));
assertTrue(int.class.isAssignableFrom(int.class));
assertFalse(float.class.isAssignableFrom(int.class));

As we can see, the instanceof keyword doesn’t support primitive types. If we use the isInstance method with an int value, the Java compiler will transform the int value into an Integer object. So, the isInstance method supports primitive types but always returns false. When we use the isAssignableFrom method, the result depends on the exact type values.

我们可以看到,instanceof关键字并不支持原始类型。如果我们用isInstance方法处理一个int值,Java编译器会将int值转化为Integer对象。所以,isInstance方法支持原始类型,但总是返回false。当我们使用isAssignableFrom方法时,其结果取决于确切的类型值。

Thirdly, they differ with class instance variables:

第三,它们与类实例变量不同。

Shape shape = new Triangle();
Triangle triangle = new Triangle();
Class<?> clazz = shape.getClass();

assertFalse(triangle instanceof clazz); // illegal
assertTrue(clazz.isInstance(triangle));
assertTrue(clazz.isAssignableFrom(triangle.getClass()));

From the code snippet above, we realize that both isInstance and isAssignableFrom methods support the class instance variables, but the instanceof keyword doesn’t.

从上面的代码片段中,我们意识到isInstanceisAssignableFrom方法都支持类的实例变量,但instanceof关键字不支持。

6.3. Bytecode Differences

6.3.字节码的差异

In a compiled class file, they utilize different opcodes:

在一个编译的类文件中,它们利用不同的操作码。

  • The instanceof keyword corresponds to the instanceof opcode
  • Both the isInstance and isAssignableFrom methods will use the invokevirtual opcode

In the JVM Instruction Set, the instanceof opcode has a value of 193, and it has a two-byte operand:

JVM指令集中,instanceof操作码的值为193,并且它有一个两字节的操作数。

instanceof
indexbyte1
indexbyte2

Then, the JVM will compute (indexbyte1 << 8) | indexbyte2 into an index. And this index points to the run-time constant pool of the current class. At the index, the run-time constant pool contains a symbolic reference to a CONSTANT_Class_info constant. And, this constant is exactly the value that the right-hand side of the instanceof keyword needs.

然后,JVM将计算(indexbyte1 << 8) | indexbyte2成一个index。而这个index指向当前类的运行时常量库。在索引处,运行时常量池包含一个对CONSTANT_Class_info常量的符号引用。而且,这个常量正是instanceof关键字的右侧所需要的值。

Furthermore, it also explains why the instanceof keyword can’t use the class instance variable. This is because the instanceof opcode needs a constant type in the run-time constant pool, and we can’t replace a constant type with a class instance variable.

此外,它还解释了为什么instanceof关键字不能使用类实例变量。这是因为instanceof操作码需要在运行时常量池中有一个常量类型,而我们不能用类实例变量替换常量类型。

And, where is the left-hand side object information of the instanceof keyword stored? It’s on the top of the operand stack. So, the instanceof keyword requires an object on the operand stack and a constant type in the run-time constant pool.

还有,instanceof关键字的左侧对象信息存储在哪里?它在操作数栈的顶部。所以,instanceof关键字需要在操作数栈上有一个对象,在运行时常量池中有一个常量类型。

In the JVM Instruction Set, the invokevirtual opcode has a value of 182, and it also has a two-byte operand:

JVM指令集中,invokevirtual操作码的值为182,它也有一个两字节的操作数。

invokevirtual
indexbyte1
indexbyte2

Similarly, the JVM will calculate (indexbyte1 << 8) | indexbyte2 into an index. At the index, the run-time constant pool holds a symbolic reference to a CONSTANT_Methodref_info constant. This constant contains the target method information, such as class name, method name, and method descriptor.

同样地,JVM将计算(indexbyte1 << 8) | indexbyte2index。在index处,运行时常量池持有对CONSTANT_Methodref_info常量的符号引用。这个常量包含了目标方法的信息,例如类名、方法名和方法描述符。

The isInstance method requires two elements on the operand stack: The first element is the type; the second element is the object. However, the isAssignableFrom method requires two type elements on the operand stack.

isInstance方法需要操作数堆栈中的两个元素。第一个元素是类型;第二个元素是对象。然而,isAssignableFrom方法在操作数栈上需要两个类型元素。

6.4. Summing Up

6.4.总结

In summary, let’s use a table to illustrate their differences:

综上所述,让我们用一个表格来说明它们的区别。

Property instanceof Class.isInstance Class.isAssignableFrom
Kind keyword native method native method
Operands An object and a type A type and an object One type and another type
Null handling false false NullPointerException
Primitive types Not Supported Supported, but always false Yes
Class instance variable No Yes Yes
Bytecode instanceof invokevirtual invokevirtual
Most suitable when The object is given, type is known at compile time The object is given, the target type is not known at compile type No object is given, only types are known and only at runtime
Use cases Daily use, suitable for the majority of cases Complex and untypical cases such as implementing a library or a utility with the use of Reflection API

7. Conclusion

7.结语

In this tutorial, we looked at the instanceof, Class.isInstance, and Class.isAssignableFrom methods and explored their usage and differences.

在本教程中,我们研究了instanceofClass.isInstanceClass.isAssignableFrom方法,并探讨了它们的用法和区别。

As always, the example code for this tutorial can be found over on GitHub.

一如既往,本教程的示例代码可以在GitHub上找到over