Back to Object Class & Constructors
Q4Question 4 of 5

What is instanceof and pattern matching?

Key Concepts
  • instanceof checks if an object is of a particular type at runtime, returning true or false
  • null instanceof AnyType always returns false, making instanceof null-safe
  • Pre-Java-16: check with instanceof, then explicitly cast (two redundant steps)
  • Java 16+ pattern matching: if (obj instanceof SavingsAccount sa) combines check, cast, and bind in one step
  • The pattern variable is flow-scoped: available only where the compiler can prove the type is correct
  • Java 21 switch with pattern matching enables exhaustive type handling with sealed hierarchies

Definition

instanceof checks type at runtime. Pre-Java-16: check plus explicit cast (two steps). Java 16+: instanceof SavingsAccount sa does check, cast, and bind in one step. Flow-scoped variable. Null-safe.

instanceof checks if an object IS-A particular type at runtime.

  • Pre-Java-16: after the check, you must explicitly cast to access the type's methods: if (account instanceof SavingsAccount) { SavingsAccount sa = (SavingsAccount) account; }.
  • Java 16+ pattern matching: the cast and variable binding happen in the instanceof expression: if (account instanceof SavingsAccount sa) { sa.getInterestRate(); }. No explicit cast needed.
  • Flow-scoped: the variable sa is available only where the compiler can prove the type is correct (inside the if-block, after && guards).
  • Null-safe: null instanceof AnyType always returns false.
  • Java 21 switch: pattern matching in switch enables exhaustive type handling with sealed hierarchies and when guards.

Old way: the clerk at National Bank of India checks Rajesh's ID: is this a savings account? Yes. Then the clerk manually writes Savings Account on a separate slip and reads from the slip. Two steps: check plus cast. New way (pattern matching): is this a savings account? If so, call it sa. One step: the check and the variable are combined.

Understanding the Concepts

What is instanceof and pattern matching?

instanceof checks if an object is of a particular type at runtime. Pattern matching eliminates the redundant cast.

How did it work before Java 16?

JAVA
public void processAccount(Account account) {
    if (account instanceof SavingsAccount) {
        SavingsAccount sa = (SavingsAccount) account; // explicit cast: redundant
        java.math.BigDecimal interest = sa.calculateInterest();
        sa.deposit(interest);
    } else if (account instanceof CurrentAccount) {
        CurrentAccount ca = (CurrentAccount) account; // another explicit cast
        ca.checkOverdraft();
    }
    // Problems: redundant (just checked instanceof), error-prone (copy-paste wrong cast), verbose.
}

How does Java 16+ pattern matching work?

JAVA
public void processAccount(Account account) {
    if (account instanceof SavingsAccount sa) {
        // sa is already cast to SavingsAccount: no explicit cast
        java.math.BigDecimal interest = sa.calculateInterest();
        sa.deposit(interest);
    } else if (account instanceof CurrentAccount ca) {
        ca.checkOverdraft();
    }
    // One expression does check + cast + bind. Cleaner. Safer. Shorter.
}

How does flow scoping work?

JAVA
// Inside if-block: sa is in scope
if (account instanceof SavingsAccount sa) {
    sa.calculateInterest(); // valid: type proven by instanceof
}
// sa is NOT available here: out of scope

// With && guard: sa is in scope for the right side
if (account instanceof SavingsAccount sa && sa.getBalance().compareTo(threshold) > 0) {
    sa.calculateInterest(); // valid: instanceof passed, && guarantees sa is bound
}

// With || : sa is NOT in scope (type not proven)
// if (account instanceof SavingsAccount sa || sa.getBalance()...) // compile error

// Negated condition: sa available after early return
if (!(account instanceof SavingsAccount sa)) {
    return; // exit: instanceof failed
}
sa.calculateInterest(); // valid: if we reached here, instanceof passed

How does Java 21 switch pattern matching work?

JAVA
public String describeAccount(Account account) {
    return switch (account) {
        case SavingsAccount sa when sa.getBalance().compareTo(new java.math.BigDecimal("100000")) > 0
            -> "Premium Savings: " + sa.getAccountId();
        case SavingsAccount sa
            -> "Regular Savings: " + sa.getAccountId();
        case CurrentAccount ca
            -> "Current: " + ca.getAccountId();
        case null
            -> "No account provided";
        default
            -> "Unknown account type";
    };
    // Each case: type check + cast + bind + optional guard (when clause)
}

How does instanceof handle null?

JAVA
Account account = null;
boolean result = account instanceof SavingsAccount; // false: null-safe
// No NullPointerException. null instanceof AnyType is always false.

What are common mistakes?

The most common mistake is using || with pattern matching, which does not guarantee the type.

JAVA
// WRONG: || does not guarantee sa is bound
// if (account instanceof SavingsAccount sa || sa.getBalance() > 0) // compile error

// CORRECT: use && (type guaranteed for right side)
if (account instanceof SavingsAccount sa && sa.getBalance().signum() > 0) { /* valid */ }

What should a senior engineer know?

  • Pattern matching in switch with sealed classes enables exhaustive handling: no default needed, and adding a new subtype causes compile errors.
  • Record patterns (Java 21) enable destructuring: case Trade(String id, BigDecimal amount) extracts fields directly.
  • In Spring, pattern matching is used in exception handlers and event processors to handle polymorphic hierarchies concisely.

Visual Overview

DIAGRAM
  BEFORE Java 16            AFTER Java 16
  ┌─────────────────┐      ┌─────────────────┐
  │ if (obj          │      │ if (obj          │
  │   instanceof     │      │   instanceof     │
  │   SavingsAccount)│      │   SavingsAccount │
  │ {                │      │   sa)            │
  │   SavingsAccount │      │ {                │
  │   sa = (Savings  │      │   sa.calculate() │
  │   Account) obj;  │      │ }                │
  │   sa.calculate() │      │                  │
  │ }                │      │ 1 step:          │
  │                  │      │ check + cast     │
  │ 2 steps:         │      │ + bind           │
  │ check + cast     │      │                  │
  └─────────────────┘      └─────────────────┘

Key Takeaway

The instanceof operator checks if an object is of a particular type at runtime. Before Java 16, you check with instanceof and then explicitly cast: two steps, redundant, and error-prone. Java 16+ pattern matching combines check, cast, and variable binding in one expression: if (account instanceof SavingsAccount sa). The variable sa is automatically typed and flow-scoped: available only where the compiler can prove the type is correct. The switch form (Java 21) is the most powerful: case SavingsAccount sa when condition handles type check, cast, bind, guard, and exhaustiveness in one construct.

Remember one sentence: pattern matching for instanceof (Java 16+) combines the type check, cast, and variable binding in one expression, eliminating redundant casts and an entire category of ClassCastException bugs.

---

Share
Use ← → arrow keys to navigate