1. 简介

在这篇快速教程中,我们将深入探讨 Java 中的 instanceof 操作符。

2. 什么是 instanceof 操作符?

instanceof 是一个二元操作符,用于判断一个对象是否属于某个特定类型。
它的返回值是布尔类型:truefalse。由于其主要功能是进行类型比较,因此也被称为 类型比较操作符

在对未知类型的对象进行 类型转换 前,务必使用 instanceof 进行检查,这样可以有效避免运行时抛出 ClassCastException 异常。

基本语法如下:

(object) instanceof (type)

示例演示:

我们先定义一个类 Round

public class Round {
    // 实现细节
}

然后定义一个继承自 Round 的类 Ring

public class Ring extends Round {
    // 实现细节
}

我们可以使用 instanceof 来验证 Ring 实例是否属于 Round 类型:

@Test
void givenWhenInstanceIsCorrect_thenReturnTrue() {
    Ring ring = new Ring();
    assertTrue(ring instanceof Round);
}

3. instanceof 是如何工作的?

instanceof 的判断依据是“is-a”关系,即基于类的继承关系或接口实现关系。

为了说明这一点,我们创建一个接口 Shape

public interface Shape {
    // 实现细节
}

再定义一个类 Circle,它继承 Round 并实现 Shape 接口:

public class Circle extends Round implements Shape {
    // 实现细节
}

各种情况下的判断结果:

✅ 对象是该类型的实例:

@Test
void givenWhenObjectIsInstanceOfType_thenReturnTrue() {
    Circle circle = new Circle();
    assertTrue(circle instanceof Circle);
}

✅ 对象是该类型的子类实例:

@Test
void givenWhenInstanceIsOfSubtype_thenReturnTrue() {
    Circle circle = new Circle();
    assertTrue(circle instanceof Round);
}

✅ 类型是接口,对象实现了该接口:

@Test
void givenWhenTypeIsInterface_thenReturnTrue() {
    Circle circle = new Circle();
    assertTrue(circle instanceof Shape);
}

❌ 如果两个类型之间没有任何关系,则不能使用 instanceof

我们定义一个类 Triangle,它也实现了 Shape,但与 Circle 没有继承关系:

public class Triangle implements Shape {
    // 实现细节
}

尝试判断 Circle 是否是 Triangle 类型:

@Test
void givenWhenComparingClassInDiffHierarchy_thenCompilationError() {
    Circle circle = new Circle();
    assertFalse(circle instanceof Triangle);
}

这会导致编译错误:

java.lang.Error: Unresolved compilation problem:
  Incompatible conditional operand types Circle and Triangle

⚠️ 编译器会直接报错,因为两个类型之间没有继承或实现关系。

4. 与 Object 类型一起使用 instanceof

在 Java 中,所有类都隐式继承自 Object 类。因此,**任何对象与 Object 类型进行 instanceof 判断都会返回 true**。

@Test
void givenWhenTypeIsOfObjectType_thenReturnTrue() {
    Thread thread = new Thread();
    assertTrue(thread instanceof Object);
}

5. 对 null 使用 instanceof

如果对象为 null,**instanceof 总是返回 false。而且,不需要额外做 null 判断**,instanceof 自带空值保护。

@Test
void givenWhenInstanceValueIsNull_thenReturnFalse() {
    Circle circle = null;
    assertFalse(circle instanceof Round);
}

6. instanceof 与泛型

⚠️ Java 的泛型在运行时会被擦除(Type Erasure),因此不能在泛型类型上直接使用 instanceof

例如,以下代码是非法的:

public static <T> void sort(List<T> collection) {
    if (collection instanceof List<String>) {
        // sort strings differently
    }
        
    // omitted
}

编译器会报错:

error: illegal generic type for instanceof
        if (collection instanceof List<String>) {
                                      ^

在 Java 中,只有“可具体化类型(reifiable types)”才能用于 instanceof 操作
可具体化类型是指在运行时保留了完整类型信息的类型。

Java 中的可具体化类型包括:

  • 基本类型,如 int
  • 非泛型类和接口,如 StringRandom
  • 所有类型参数为无界通配符的泛型类型,如 Set<?>Map<?, ?>
  • 原始类型,如 ListHashMap
  • 由其他可具体化类型组成的数组,如 String[]List[]Map<?, ?>[]

由于泛型参数本身不是可具体化类型,以下写法是非法的:

public static <T> boolean isOfType(Object input) {
    return input instanceof T; // ❌ 编译不通过
}

✅ 但可以使用通配符形式:

if (collection instanceof List<?>) {
    // do something
}

7. Stream API 中使用 instanceof 进行类型过滤

Java 8 引入的 Stream API 提供了强大的集合操作能力。在使用 map() 方法进行类型转换时,如果涉及类型转换(casting),建议先使用 instanceof 进行类型检查,避免 ClassCastException 异常

示例场景:

假设我们有一个 Round 类型的 Stream

Stream<Round> roundStream = Stream.of(new Ring(), new Ring(), new Circle());

这个流中包含两个 Ring 和一个 Circle 实例。

如果我们不进行类型检查就直接转换为 Ring 列表:

@Test
void givenWhenStream_whenCastWithoutInstanceOfChk_thenGetException() {
    Stream<Round> roundStream = Stream.of(new Ring(), new Ring(), new Circle());
    assertThrows(ClassCastException.class, () -> roundStream.map(it -> (Ring) it).collect(Collectors.toList()));
}

就会抛出 ClassCastException,因为 Circle 不能强制转换为 Ring

✅ 正确的做法是先使用 instanceof 过滤:

@Test
void givenWhenStream_whenCastAfterInstanceOfChk_thenGetExpectedResult() {
    Stream<Round> roundStream = Stream.of(new Ring(), new Ring(), new Circle());
    List<Ring> ringList = roundStream
        .filter(it -> it instanceof Ring)
        .map(it -> (Ring) it)
        .collect(Collectors.toList());
    assertEquals(2, ringList.size());
}

8. 总结

本文我们系统介绍了 Java 中的 instanceof 操作符,包括其基本用法、工作原理、与泛型和 Stream API 的结合使用等内容。

完整示例代码可参考:GitHub 项目地址


» 下一篇: Derive4J 入门指南