Bootstrap

instanceof关键字深入理解

深入理解 instanceof 关键字

1. 基本概念回顾

instanceof 运算符用于判断一个对象是否是指定类或接口的实例,或者是否是该类的子类、该接口的实现类的实例。它返回一个布尔值,若对象是指定类型的实例则返回 true,否则返回 false。其基本语法为:

object instanceof Class/Interface

这里出现第一个注意点

  • 左侧操作数:必须是一个对象引用表达式,可以是一个变量、方法调用的返回值或者其他能产生对象引用的表达式。例如多态中的父类引用指向子类对象,其中左边的父类就是引用类型。
  • 右侧操作数:必须是一个类、接口的名称。

直接说instanceof返回true还是false的结论

如果在此次实例过程中左侧的操作数(即引用类型)与右侧的操作数(实例对象)存在关系则返回true,反之则返回false。

具体是什么意思呢?

例如,父类Person和子类Student:

Person person = new Student();

在这个过程中是不是实例化了一个Student类,那么实例化子类的过程中必定先构造父类,即在这个过程中其实是先构造所有类的父类Object–>再构造Person–>再构造的Student。那么我就称在此次实例过程中Object、Person、Student类有关系,所以接下来的判断中只要左边的引用类型是这三个类其中的一种,右边的实例类型也是这三个的一种,都会返回true(即只要左右有关——这里的有关指继承、实现关系,就返回true)。

但是要注意

因为Object类是所有类的父类,那么Object类其实也是String类的父类,按理说用instanceof也会返回true,但是我们一定要注意定语在这一次的实例过程中,我们在这一次的实例过程中只跟Object–>Person–>Student有关,所以涉及String的都会返回false。

易错点

class Person {}
class Student extends Person {}

public class Main {
    public static void main(String[] args) {
        Student student = new Student();
        Person person = student;

        // 以下情况返回 true,这个就是讲的两边有关就true
        System.out.println(student instanceof Student); 
        System.out.println(student instanceof Person); 
        System.out.println(student instanceof Object); 
        System.out.println(person instanceof Student); 
        System.out.println(person instanceof Person); 
        System.out.println(person instanceof Object); 

        // 错误示例,如果创建一个新的 Person 对象
        Person anotherPerson = new Person();
        // 这里返回 false,因为 anotherPerson 不是 Student 类型
        System.out.println(anotherPerson instanceof Student); 
    }
}

解释第二个实例过程为什么会返回false:

这里为什么返回false呢,因为我这个实例化过程其实只是与Object–>Person有关,与Student无关,所以判断与Student有关的自然而然就返回false了。

2. 详细示例及解释
示例 1:类的继承关系中的 instanceof
// 定义一个父类
class Vehicle {
    public void move() {
        System.out.println("Vehicle is moving");
    }
}

// 定义一个子类,继承自 Vehicle
class Car extends Vehicle {
    public void honk() {
        System.out.println("Car is honking");
    }
}

public class InstanceofExample1 {
    public static void main(String[] args) {
        // 创建一个 Car 对象
        Car car = new Car();
        // 创建一个 Vehicle 引用指向 Car 对象
        Vehicle vehicle = new Car();

        // 检查 car 是否是 Car 类的实例
        System.out.println("car instanceof Car: " + (car instanceof Car)); 
        // 检查 car 是否是 Vehicle 类的实例
        System.out.println("car instanceof Vehicle: " + (car instanceof Vehicle)); 
        // 检查 vehicle 是否是 Car 类的实例
        System.out.println("vehicle instanceof Car: " + (vehicle instanceof Car)); 
        // 检查 vehicle 是否是 Vehicle 类的实例
        System.out.println("vehicle instanceof Vehicle: " + (vehicle instanceof Vehicle)); 
    }
}

输出结果

car instanceof Car: true
car instanceof Vehicle: true
vehicle instanceof Car: true
vehicle instanceof Vehicle: true

解释

  • carCar 类的实例,由于 Car 继承自 Vehicle,所以 car 也是 Vehicle 类的实例。(即我说的实例化过程中会先构造父类,所以也与父类有关,即使左边和右边都没有涉及父类)
  • vehicle 引用指向的实际对象是 Car 类的实例,因此 vehicle 既是 Car 类的实例,也是 Vehicle 类的实例。
示例 2:接口实现关系中的 instanceof(与继承关系类似的)
// 定义一个接口
interface Flyable {
    void fly();
}

// 定义一个类实现 Flyable 接口
class Bird implements Flyable {
    @Override
    public void fly() {
        System.out.println("Bird is flying");
    }
}

public class InstanceofExample2 {
    public static void main(String[] args) {
        // 创建一个 Bird 对象
        Bird bird = new Bird();
        // 创建一个 Flyable 引用指向 Bird 对象
        Flyable flyable = new Bird();

        // 检查 bird 是否是 Bird 类的实例
        System.out.println("bird instanceof Bird: " + (bird instanceof Bird)); 
        // 检查 bird 是否是 Flyable 接口的实例
        System.out.println("bird instanceof Flyable: " + (bird instanceof Flyable)); 
        // 检查 flyable 是否是 Bird 类的实例
        System.out.println("flyable instanceof Bird: " + (flyable instanceof Bird)); 
        // 检查 flyable 是否是 Flyable 接口的实例
        System.out.println("flyable instanceof Flyable: " + (flyable instanceof Flyable)); 
    }
}

输出结果

bird instanceof Bird: true
bird instanceof Flyable: true
flyable instanceof Bird: true
flyable instanceof Flyable: true

解释

  • birdBird 类的实例,同时 Bird 类实现了 Flyable 接口,所以 bird 也是 Flyable 接口的实例。
  • flyable 引用指向的实际对象是 Bird 类的实例,因此 flyable 既是 Bird 类的实例,也是 Flyable 接口的实例。

怎么和继承一样理解?你这样想:我实现一个接口,肯定得先知道这个接口,那是不是就先构造接口,那不就是接口–>实现接口的类,即接口与实现接口的类有关系,即使是Bird bird = new Bird(),左右两边都没有涉及接口类,但是由于我这个Bird类里面本来就是会实现接口中的方法,所以还是有关系(和上述示例1中的Car car = new Car()类似)。

示例 3:null 值与 instanceof

这个比较特殊,就是如果引用类型为null,那么不论如何instanceof都返回false。

public class InstanceofExample3 {
    public static void main(String[] args) {
        Object obj = null;
        // 检查 null 对象是否是 Object 类的实例
        System.out.println("obj instanceof Object: " + (obj instanceof Object)); 
    }
}

输出结果

obj instanceof Object: false

解释null 表示没有引用任何对象,所以 null 不是任何类或接口的实例,instanceofnull 操作始终返回 false

应用
示例1:使用 instanceof 进行类型转换前的检查
class Animal {
    public void makeSound() {
        System.out.println("Animal makes a sound");
    }
}

class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Meow");
    }

    public void scratch() {
        System.out.println("Cat is scratching");
    }
}

public class InstanceofExample4 {
    public static void main(String[] args) {
        Animal animal = new Cat();

        // 检查 animal 是否是 Cat 类的实例
        if (animal instanceof Cat) {
            // 进行向下转型
            Cat cat = (Cat) animal;
            cat.makeSound();
            cat.scratch();
        }
    }
}

输出结果

Meow
Cat is scratching

解释

  • 首先创建一个 Animal 引用指向 Cat 类的实例。
  • 使用 instanceof 检查 animal 是否是 Cat 类的实例,如果是,则进行向下转型,将 animal 转换为 Cat 类型,然后调用 Cat 类特有的方法 scratch()
示例 2:结合泛型使用 instanceof
import java.util.ArrayList;
import java.util.List;

public class InstanceofExample5 {
    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();
        // 检查 stringList 是否是 List 接口的实例
        System.out.println("stringList instanceof List: " + (stringList instanceof List)); 
        // 注意:泛型在运行时会被擦除,不能使用泛型类型进行 instanceof 检查
        // 下面这行代码会编译错误
        // System.out.println("stringList instanceof List<String>: " + (stringList instanceof List<String>)); 
    }
}

输出结果

stringList instanceof List: true

解释

  • stringListArrayList 类的实例,而 ArrayList 实现了 List 接口,所以 stringListList 接口的实例。
  • 由于 Java 的泛型在运行时会被擦除,不能使用泛型类型进行 instanceof 检查,否则会导致编译错误。
3. Java 14 及以后的模式匹配 instanceof

在 Java 14 及以后的版本中,引入了模式匹配的 instanceof 语法,它可以在检查类型的同时进行类型转换,使代码更加简洁。

class Fruit {
    public void taste() {
        System.out.println("Fruit has a taste");
    }
}

class Apple extends Fruit {
    @Override
    public void taste() {
        System.out.println("Apple is sweet");
    }

    public void makeJuice() {
        System.out.println("Making apple juice");
    }
}

public class PatternMatchingInstanceOfExample {
    public static void main(String[] args) {
        Fruit fruit = new Apple();

        // 使用模式匹配的 instanceof
        if (fruit instanceof Apple apple) {
            apple.taste();
            apple.makeJuice();
        }
    }
}

输出结果

Apple is sweet
Making apple juice

解释

  • if 语句中,使用模式匹配的 instanceof 语法检查 fruit 是否是 Apple 类的实例**,如果是,则将其转换为 Apple 类型并赋值给 apple 变量,然后可以直接使用 apple 变量调用 Apple 类的方法。( 即实现了自动转换,其中Apple apple = (Apple)fruit
;