skip to content

What is switch pattern matching in Java, and how do type patterns in case labels differ from the traditional constant-based switch?

level: juniorimportance: must knowfreq 60%

answer

  1. case Type var = type pattern, binds + no cast
  2. selector can be any reference type, not just int/enum/String
  3. arrow form = no fallthrough, can be an expression
  4. Java 21 standard (JEP 441)
  5. replaces instanceof if-else ladders

basics

~20 s

Switch pattern matching lets each case test the type of the value instead of comparing it to a constant. A type pattern like case Circle c matches when the value is a Circle and binds it to c, which you can use directly in that branch.

solid answer

~40 s

Traditional switch compares the selector against constant labels (ints, enums, strings) for equality. Switch pattern matching (standardized in Java 21) lets a case label be a type pattern, e.g. case Integer i, which matches when the runtime type of the selector is Integer and binds it to a new variable i scoped to that case. This replaces the old chain of if (x instanceof T t) ... else if ... with a single, readable switch. It works as both a statement and an expression. The selector can be any reference type, not just int/enum/String. Combined with sealed types it gives compiler-checked exhaustiveness, and each arm can further refine the match with guards (when) or destructure records. It is a major step toward data-oriented programming in Java.

code

java · 11 lines
java
sealed interface Shape permits Circle, Square {}
record Circle(double r) implements Shape {}
record Square(double side) implements Shape {}

static double area(Shape s) {
    return switch (s) {           // selector is a reference type
        case Circle c -> Math.PI * c.r() * c.r();  // type pattern binds c
        case Square sq -> sq.side() * sq.side();
        // no default needed: Shape is sealed -> exhaustive
    };
}

go deeper

for a junior

Knows a case label can be a type like case Integer i, that it binds the value with no cast, and that the arrow form has no fallthrough.

for a middle

Explains the selector can be any reference type, distinguishes statement vs expression switch, and knows type patterns don't match null without case null.

for a senior

Frames it against instanceof ladders, ties it to sealed-type exhaustiveness and guards/record patterns, and notes the Java 21 timeline and runtime-type semantics.

for a principal

Positions it within data-oriented programming, weighs pattern switches vs the visitor pattern for extensibility, and reasons about API/library evolution and exhaustiveness guarantees as a design contract.

## The problem it solves A **switch** is a control-flow statement that picks one branch based on a value (the *selector*). The classic switch only allowed **constant** labels: `case 1:`, `case "OPEN":`, `case MONDAY:` — each `case` is an equality test against a compile-time constant. If you wanted to branch on the *type* of an object, you had to write a ladder of `if (x instanceof Circle) { Circle c = (Circle) x; ... } else if (x instanceof Square) { ... }`. This is verbose and error-prone (you cast manually, you can forget a type). ## Type patterns **Pattern matching** (general feature, finalized for `switch` in Java 21, JEP 441) lets a `case` label be a **type pattern** instead of a constant. A type pattern is written `Type variableName`: ```java String describe(Object obj) { return switch (obj) { case Integer i -> "int " + i; // matches if obj is an Integer, binds it to i case String s -> "string " + s.length(); default -> "other"; }; } ``` When the selector's **runtime type** is assignable to the pattern's type, the case matches and the value is bound to the **pattern variable** (`i`, `s`). That variable is already the right type — no cast — and is **scoped to its own case arm**. ### Key terms - **Selector**: the value in `switch (selector)`. With patterns it can be any reference type (e.g. `Object`, an interface), not just `int`/`char`/`enum`/`String`. - **Runtime type**: the actual class of the object at execution time, which may be a subtype of the variable's declared (static) type. - **Pattern variable**: the binding introduced by the pattern (`i` in `case Integer i`). It is effectively final and only in scope where the match is guaranteed. ## Statement vs expression Switch works two ways. As a **statement** it just runs side effects. As a **switch expression** (using the arrow `->` form) it *produces a value*, so you can do `String s = switch (obj) { ... };`. The arrow form also has **no fallthrough** — exactly one arm runs — which removes the classic missing-`break` bug. ## Why it matters Type-pattern switches collapse `instanceof` ladders into one structure, and when the selector type is a **sealed** hierarchy (a closed set of permitted subtypes) the compiler can verify you covered every case (**exhaustiveness**), letting you omit `default`. Arms can be refined with **guards** (`case Integer i when i > 0`) and **record patterns** (destructuring). Together these enable *data-oriented programming*: model data as records + sealed interfaces, then process it with one exhaustive switch. ## Caveats - Available as a standard feature from **Java 21** (preview in 17–20). - A type pattern for a non-nullable case does **not** match `null` — you need an explicit `case null` (covered separately). - Order matters: a more general pattern must not come before a more specific one it would shadow (dominance).

  • Does a type pattern case match null?
    No. A type pattern such as case String s does not match null; passing null falls through to default (if present) or throws NullPointerException when the switch has no null handling. You must add an explicit case null to match it.
  • From which Java version is switch pattern matching a standard (non-preview) feature?
    Java 21 (JEP 441). It was available as a preview in Java 17 through 20.

Old switch is a coat-check that only recognizes ticket numbers; pattern switch is a bouncer who looks at what kind of thing walks in (a Circle? a String?) and hands it to the right room already labeled.

saying these in an interview costs you the question

  • Thinking a plain type pattern like case String s also matches null — it does not.
  • Assuming pattern switch works on primitives or only on int/enum/String selectors; the selector is any reference type.
  • Believing case ordering is free — a general pattern before a specific one is a compile error (dominance).
  • Confusing the arrow form (no fallthrough, can be an expression) with the legacy colon form.

context