Skip to content
Java polymorphism 6 min read

instanceof Operator

The instanceof operator answers one simple question: does this object belong to a given type? It returns true or false and is most often used right before a cast so you never get a surprise ClassCastException at runtime.

Basic Syntax

object instanceof ClassName

If object is an instance of ClassName (or any subclass / implementing class of it), the expression evaluates to true. If object is null, it always evaluates to false — no NullPointerException.

class Animal {}
class Dog extends Animal {}

public class Main {
    public static void main(String[] args) {
        Animal a = new Dog();

        System.out.println(a instanceof Dog);    // true
        System.out.println(a instanceof Animal); // true — Dog IS-A Animal
        System.out.println(a instanceof Object); // true — everything IS-A Object

        Animal b = null;
        System.out.println(b instanceof Dog);    // false — null is never an instance
    }
}

Output:

true
true
true
false

Note: instanceof checks the runtime type of the object, not the declared (compile-time) type of the variable. Even though a is declared as Animal, the object it points to was created as Dog, so a instanceof Dog is true.

Why You Need instanceof Before Casting

Inheritance lets you store a Dog in an Animal variable. That’s fine going up the hierarchy (upcasting). Going down (downcasting) is where bugs hide:

Animal a = new Animal(); // NOT a Dog this time

// Without instanceof check — DANGEROUS:
Dog d = (Dog) a;         // Compiles fine, crashes at runtime!
// Throws: ClassCastException: Animal cannot be cast to Dog

The safe pattern is to check, then cast:

Animal a = new Animal();

if (a instanceof Dog) {
    Dog d = (Dog) a;
    d.bark(); // safe — we confirmed the type first
} else {
    System.out.println("Not a Dog, skipping bark.");
}

Output:

Not a Dog, skipping bark.

Warning: Skipping the instanceof check before a downcast is one of the most common runtime errors in Java. Always guard casts unless you have ironclad knowledge of the actual type.

Using instanceof with Interfaces

instanceof works just as well against interfaces — it checks whether the object’s class implements the interface.

interface Flyable {
    void fly();
}

class Bird implements Flyable {
    public void fly() { System.out.println("Bird flying"); }
}

class Cat {}

public class Main {
    public static void main(String[] args) {
        Object obj1 = new Bird();
        Object obj2 = new Cat();

        if (obj1 instanceof Flyable f) { // pattern matching (Java 16+)
            f.fly();
        }

        System.out.println(obj2 instanceof Flyable); // false
    }
}

Output:

Bird flying
false

Pattern Matching with instanceof (Java 16+)

Java 16 made a quality-of-life improvement: you can declare a pattern variable right inside the instanceof test. If the test passes, the variable is automatically cast and in scope — no separate cast line needed.

class Shape {}
class Circle extends Shape {
    double radius;
    Circle(double r) { this.radius = r; }
}
class Rectangle extends Shape {
    double width, height;
    Rectangle(double w, double h) { this.width = w; this.height = h; }
}

public class Main {
    static double area(Shape s) {
        if (s instanceof Circle c) {
            return Math.PI * c.radius * c.radius;
        } else if (s instanceof Rectangle r) {
            return r.width * r.height;
        }
        return 0;
    }

    public static void main(String[] args) {
        System.out.printf("Circle area: %.2f%n", area(new Circle(5)));
        System.out.printf("Rectangle area: %.2f%n", area(new Rectangle(4, 6)));
    }
}

Output:

Circle area: 78.54
Rectangle area: 24.00

The pattern variable (c, r) is only in scope inside the if block where the check succeeded, so the compiler guarantees it is safe to use without an extra cast.

Tip: Pattern matching instanceof is finalized in Java 16. If you’re on Java 8–15, you still need the classic two-step: check then cast.

instanceof and Inheritance Hierarchy

Understanding which checks return true is crucial when you have a deeper hierarchy:

class Vehicle {}
class Car extends Vehicle {}
class ElectricCar extends Car {}

public class Main {
    public static void main(String[] args) {
        ElectricCar ec = new ElectricCar();

        System.out.println(ec instanceof ElectricCar); // true
        System.out.println(ec instanceof Car);         // true
        System.out.println(ec instanceof Vehicle);     // true
        System.out.println(ec instanceof Object);      // true

        Car c = new Car();
        System.out.println(c instanceof ElectricCar);  // false — Car is NOT an ElectricCar
    }
}

Output:

true
true
true
true
false

Think of it as asking: “Is this object at least of this type?” It succeeds for the object’s own class and every ancestor class or interface up the chain.

Comparison: Classic vs Pattern Matching

FeatureClassic (pre-16)Pattern Matching (16+)
Syntaxobj instanceof Fooobj instanceof Foo f
Requires separate cast?YesNo
Variable scopeN/AInside the if block
Null safe?YesYes
Java versionAll versionsJava 16+ (finalized)

Common Pitfall: Comparing Unrelated Types

The compiler catches one category of mistake for you: if the two types are completely unrelated and no subtyping relationship is possible, it reports a compile error.

String s = "hello";
// System.out.println(s instanceof Integer); // Compile error — String can never be Integer

However, with interfaces or generic types the compiler is more lenient. Keep this in mind when writing defensive checks.

Under the Hood

At the bytecode level, instanceof compiles to the instanceof JVM instruction (0xC1). The JVM walks the object’s class hierarchy stored in the class metadata area of the heap to determine whether the target type appears anywhere in the chain.

For pattern matching variables, the compiler generates the cast instruction automatically right after the instanceof check, so there is no extra overhead compared to the classic check-then-cast pattern — it is purely syntactic sugar.

Performance-wise, instanceof is very fast. The JIT compiler (see JIT Compilation) can often inline and optimize type checks, especially in tight loops or polymorphic call sites tracked by the JVM’s profiling data.

One subtlety: because instanceof relies on the runtime class loader chain, two classes with the same fully-qualified name loaded by different class loaders are considered distinct types. This matters in OSGi containers, plugin frameworks, and application servers — obj instanceof Foo can return false even if the class looks identical, because they are different Class objects. See Class Loaders for details.

Quick Reference

// 1. Simple check
System.out.println("hi" instanceof String); // true

// 2. Null safety
Object o = null;
System.out.println(o instanceof String);    // false (no NPE)

// 3. Classic guard-and-cast
if (animal instanceof Dog) {
    Dog d = (Dog) animal;
    d.bark();
}

// 4. Pattern matching (Java 16+)
if (animal instanceof Dog d) {
    d.bark(); // d already cast, no boilerplate
}
  • Runtime Polymorphism — how the JVM dispatches method calls at runtime, closely related to instanceof checks
  • Inheritance — understanding the type hierarchy that instanceof traverses
  • Pattern Matching — Java 21’s extended pattern matching in switch expressions builds directly on instanceof patterns
  • Casting & Types — a look at Java’s primitive and reference type system
  • vtable & Dynamic Dispatch — the low-level mechanism behind how the JVM resolves object types at runtime
  • Abstract Class vs Interface — knowing when to use abstract classes vs interfaces helps you write cleaner instanceof checks
Last updated June 13, 2026
Was this helpful?