Skip to content

Lesson 08 · Pattern Matching

Objectives

After this lesson you will be able to:

  • Use instanceof patterns with binding variables and scope.
  • Use type patterns in switch, with null and guarded (when) labels.
  • Deconstruct records with record patterns, including nesting.

instanceof patterns

A type pattern tests and binds in one step — no separate cast:

java
Object o = "hello";
if (o instanceof String s && s.length() > 3) {
    System.out.println(s.toUpperCase());   // s is in scope and definitely a String
}

The binding s is in scope only where the pattern definitely matched — this is flow scoping. After if (!(o instanceof String s)) return;, s is in scope below because control only continues when the match held.

Type patterns in switch

A switch can match on type, replacing long if/else instanceof chains:

java
String describe(Object o) {
    return switch (o) {
        case null      -> "null";
        case Integer i -> "int " + i;
        case String s  -> "str " + s.length();
        default        -> "other";
    };
}

Exam trap

A pattern switch handles null only if you add a case null (otherwise a null selector throws NPE). Pattern labels must be ordered so a more specific type comes before a supertype — an unreachable label (dominated by an earlier one) is a compile error.

Guarded patterns (when)

Add a boolean guard with when to refine a case:

java
String size(Object o) {
    return switch (o) {
        case String s when s.length() > 10 -> "long string";
        case String s                      -> "short string";
        default                            -> "not a string";
    };
}

Record patterns (deconstruction)

A record pattern matches a record and binds its components directly — and nests:

java
record Point(int x, int y) { }
record Line(Point from, Point to) { }

String f(Object o) {
    return switch (o) {
        case Line(Point(var x1, var y1), Point(var x2, var y2)) ->
            "from (" + x1 + "," + y1 + ") to (" + x2 + "," + y2 + ")";
        case Point(var x, var y) -> "point " + x + "," + y;
        default -> "?";
    };
}

SDET note

Pattern matching plus sealed types (Lesson 06) gives exhaustive, branch-complete handling the compiler verifies. For parsing/validation logic this removes a whole class of "forgot a case" bugs — exactly the kind of subtle gap to check in AI-generated switch code.

Key Takeaways

  • An instanceof type pattern tests and binds at once; the binding follows flow scoping (in scope only where the match is guaranteed).
  • A pattern switch matches by type; add case null to handle null, and order specific types before their supertypes (dominated labels won't compile).
  • when adds a boolean guard to a case label.
  • Record patterns deconstruct components (and nest), binding fields directly.

Lesson Quiz

Lesson Quiz · Pattern Matching0 / 5
  1. In if (o instanceof String s) { ... }, where is s usable?

    • AAnywhere in the method
    • BOnly inside the if block where the match holds
    • COnly in the condition
    • DNowhere — you still need a cast
  2. A pattern switch with no case null receives a null selector. What happens?

    • AMatches default
    • BReturns null
    • CNullPointerException
    • DCompile error
  3. Why does this NOT compile?

    switch (o) {
      case Object obj -> "obj";
      case String s -> "str";
    }
    • AMissing default
    • Bcase String is unreachable — dominated by case Object
    • CPatterns aren't allowed in switch
    • Dobj is unused
  4. What does the record pattern case Point(var x, var y) do?

    • ACalls Point's constructor
    • BMatches a Point and binds its components to x and y
    • CCreates a new Point
    • DCompares x and y
  5. What does when add to a case label?

    • AA loop
    • BA boolean guard condition
    • CA default
    • DException handling

Next: Module 03 Mini-Exam. Run the matching code in labs/src/main/java/com/jse21/m03_oop/.