Course topics

By WebNest Studio Engineering Team

Java - Core Tutorial

instanceof and Downcasting

Upcasting (treating a subclass object as its superclass or interface type) happens automatically and safely in Java. Downcasting — converting a superclass or interface reference back to a more specific subclass type — is not automatic, requires an explicit cast, and is only safe when the object being cast is genuinely an instance of that target type at runtime.

Why it matters

When code holds a general reference but needs something only a specific subtype offers, it has to downcast, and a wrong cast fails at runtime with ClassCastException. instanceof is the check that makes the cast safe, and modern Java combines the check and the cast into one step.

Why Downcasting Needs a Check

A reference variable's declared type limits what the compiler lets you call on it, but the actual object it points to at runtime might be any subclass. Casting a reference down to a more specific type without verifying the object's real type first risks a ClassCastException at runtime if the object is not actually of that type.

Using instanceof for Safe Downcasting

The instanceof operator checks whether an object is an instance of a given type (or a subtype of it) before you attempt the cast, returning a boolean. The traditional pattern is to check with instanceof first, then cast only if the check passes, avoiding an unguarded downcast that could throw.

Pattern Matching for instanceof (Modern Java)

Since Java 16, pattern matching for instanceof lets you combine the check and the cast in one expression: if (obj instanceof Dog dog) both tests the type and binds a new, already-cast variable (dog) usable inside that branch, eliminating the separate explicit cast statement and the boilerplate of the older two-step pattern.

Example

Safe downcasting with instanceof, including modern pattern matching syntax

Java
class Animal {
    void makeSound() {
        System.out.println("Some generic animal sound");
    }
}

class Dog extends Animal {
    void fetch() {
        System.out.println("Dog is fetching the ball");
    }
}

public class InstanceofDemo {
    public static void main(String[] args) {
        Animal a = new Dog(); // upcasting, always safe

        // Traditional check-then-cast pattern
        if (a instanceof Dog) {
            Dog d = (Dog) a;
            d.fetch();
        }

        // Modern pattern matching for instanceof (Java 16+)
        if (a instanceof Dog dog) {
            dog.fetch();
        }

        Animal generic = new Animal();
        if (!(generic instanceof Dog)) {
            System.out.println("generic is not a Dog, downcast skipped safely");
        }
    }
}
Output
Dog is fetching the ball
Dog is fetching the ball
generic is not a Dog, downcast skipped safely

Common Mistakes

  • Downcasting without an instanceof check first, risking a ClassCastException when the object is not actually of the target type.
  • Assuming instanceof returns true for null — it always returns false when checked against a null reference, by design.
  • Forgetting that upcasting (subclass to superclass) never needs an explicit cast or a check, unlike downcasting.
  • Not realizing pattern matching for instanceof only narrows the type inside the branch where the condition is true (and, with negation, appropriately outside it).

Exercise

Write describe(Object value) using pattern matching for instanceof. A String returns text of length N, an Integer returns number followed by double its value, and anything else returns unknown. This needs Java 16 or newer.

Expected output: text of length 5, number 42, unknown

Java · starter code
public class Describe {

    static String describe(Object value) {
        // TODO: use "value instanceof String text" and "value instanceof Integer number"
        return "unknown";
    }

    public static void main(String[] args) {
        System.out.println(describe("hello"));
        System.out.println(describe(21));
        System.out.println(describe(3.5));
    }
}
Show hints
  • if (value instanceof String text) tests the type and declares text already cast.
  • The literal 21 is boxed to an Integer when passed as an Object, and 3.5 becomes a Double.
Show solution
Java · solution
public class Describe {

    static String describe(Object value) {
        if (value instanceof String text) {
            return "text of length " + text.length();
        }
        if (value instanceof Integer number) {
            return "number " + (number * 2);
        }
        return "unknown";
    }

    public static void main(String[] args) {
        System.out.println(describe("hello")); // text of length 5
        System.out.println(describe(21));      // number 42
        System.out.println(describe(3.5));     // unknown
    }
}

Quick quiz

1. What does null instanceof String evaluate to?

  • A. true
  • B. false
  • C. It throws NullPointerException
  • D. It does not compile
Show answer

B. instanceof is false for null, whatever the type, so it also serves as a null check.

2. An Animal variable refers to a Cat object. What happens with (Dog) animal?

  • A. It returns null
  • B. It does not compile
  • C. It throws ClassCastException at runtime
  • D. It converts the Cat into a Dog
Show answer

C. The cast compiles because an Animal might be a Dog, but at runtime the object is a Cat, so the JVM throws ClassCastException.

3. With String s = "x";, what happens with s instanceof Integer?

  • A. It is false
  • B. It is true
  • C. It throws at runtime
  • D. It does not compile
Show answer

D. String and Integer are unrelated classes, so a String can never be an Integer. The compiler rejects the test as impossible.

Interview questions

What is the difference between upcasting and downcasting?

Show answer

Upcasting treats an object as one of its supertypes, such as a Dog as an Animal. It is always safe and happens implicitly. Downcasting goes the other way, from a general reference to a more specific type. It needs an explicit cast and can fail at runtime with ClassCastException if the object is not really of that type, so it should be guarded with instanceof.

What does pattern matching for instanceof improve?

Show answer

It combines the type test, the cast and the variable declaration. Instead of checking obj instanceof String and then writing String s = (String) obj, you write obj instanceof String s. That removes the repeated type name and the chance of casting to a different type from the one you tested.

Key Points to Remember

  • Upcasting is automatic and safe; downcasting requires an explicit cast and can throw ClassCastException.
  • instanceof checks an object's runtime type before a downcast is attempted, returning false (not an error) for null.
  • Pattern matching for instanceof (Java 16+) merges the check and cast into a single, more concise expression.
  • Always prefer checking with instanceof over an unguarded cast when the object's real type is not already certain.

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering