Switch Expressions in Java — Arrow Syntax, yield, Exhaustiveness
Fast recall
Quick revision
Core answer
switch produce a value, eliminate fall-through bugs, and require exhaustive coverage. The new arrow syntax — case A -> result — evaluates one branch and returns a value; no `break` required. Multiple labels per case: case MONDAY, TUESDAY, WEDNESDAY -> "Weekday" replaces the old fall-through pattern. For multi-statement blocks, use `yield` to return a value from the block: case X -> { ... yield value; }. Switch expressions must be exhaustive — the compiler verifies all possible inputs are covered (no unhandled case, or a default). For enums, listing every constant satisfies exhaustiveness without default; for sealed types (Java 17+), listing every permitted subtype does the same. Old switch statements still work unchanged for backward compatibility, and you can use the arrow syntax in statements too. The deeper win is conceptual: switch becomes a first-class expression that can be used in return, in field initializers, and in method arguments — not just a control structure. This is the foundation that Java 21 pattern matching for switch (JEP 441) builds on, adding type patterns, record deconstruction, and when guards. Together they push Java toward algebraic-data-type-style matching seen in Scala and Kotlin.Whether the candidate knows arrow vs colon syntax, `yield` for blocks, multiple-label cases, exhaustiveness rules, and the connection to Java 21 pattern matching
Using `break` with arrow syntax (not needed). Confusing `yield` with `return`. Not knowing exhaustive enum switches drop the `default`. Mixing arrow and colon cases in one switch (compile error)
Mention that switch expressions are the foundation for Java 21 pattern matching, explain how exhaustive sealed-type switches eliminate `default`, discuss the bytecode improvements, reference JEP 361
Interview-ready guide
Interview answer
Switch expressions (Java 14, JEP 361) fix the long-standing problems with Java's switch statement.
Three big improvements
1. Arrow syntax
case A -> result— nobreak, no fall-through- Multiple labels:
case A, B, C -> result - Each case is independent
2. Returns a value
- Switch becomes an expression — usable in
return, field initializers, method args - No need to declare a variable outside and mutate inside
3. Exhaustive
- Compiler verifies all cases covered
- Adding a new enum constant or sealed subtype → compile error in every switch
- Massive maintenance win
`yield` for multi-statement cases
- Use a block when you need multiple statements
yieldreturns the value from the block to the switch expressioncase X -> { log.info(...); yield value; }yieldvsreturn— yield exits the case block; return exits the method
Exhaustiveness rules
For enums — listing all constants is exhaustive
- No
defaultneeded - Adding a new constant → compile error in every switch
- This is the biggest win for evolving codebases
For sealed types (Java 17+) — listing all permitted subtypes is exhaustive
- No
defaultneeded - Foundation for ADT-style matching
For other types (int, String, etc.) — must include default
- Compiler can't enumerate all values
Arrow in statements too
switch (event) { case CLICK -> handleClick(event); ... }— no return value- Still no fall-through, still no break
- Useful for side-effect-only dispatch
Forbidden mixing
- Cannot mix arrow and colon cases in one switch
- Compile error
- Pick one style per switch
Switch statement vs expression — which to use
Switch expression — when you want a value
- Exhaustive (compiler-checked)
- Cleaner
- One-liner for many cases
Switch statement (arrow) — side effects only
- No value, but still safe (no fall-through)
- For event dispatch, etc.
Switch statement (colon + break) — legacy or fall-through needed
- Rare; usually a smell
- Old code; not for new
Java 21 pattern matching — the deeper payoff
Type patterns
case Integer i -> ...— bindsito the matched typecase String s -> ...case null -> ...— null is its own case
Record deconstruction (JEP 440)
case Point(int x, int y) -> ...— destructure inline- Nested:
case Line(Point(int x1, int y1), Point(int x2, int y2)) -> ...
`when` guards
case Integer i when i > 100 -> "large"- Add a predicate to a type pattern
Sealed types + pattern switch = ADT matching
- Compiler verifies exhaustiveness over the closed hierarchy
- No
defaultneeded - Same expressive power as Scala/Kotlin sealed match
Migration tips
- IDEs auto-convert old switch — generally safe
- Verify edge cases (especially intentional fall-through, which is rare)
- Remove
defaultfrom enum/sealed switches — keep exhaustiveness check - Use
default -> throw new IllegalStateException(...)only as a defensive marker for impossible cases
Best practices
- Always prefer expression over statement when value is needed
- Use arrow everywhere — avoid colon
yieldonly when you need a block- Don't add
defaultto enum/sealed switches - Group related labels with commas
- Extract complex case logic to methods
Recommendation: Use switch expressions with arrow syntax as your default for any 'compute a value based on a discriminator' pattern. They eliminate fall-through bugs, force exhaustiveness, and make the intent ("this is computing a result") obvious. For enum discriminators, leave off default so the compiler will alert you when new constants are added — this is the most powerful maintenance feature. For sealed types (Java 17+), do the same; combined with pattern matching for switch in Java 21, you get full ADT-style matching with compile-time exhaustiveness, putting Java on par with Scala and Kotlin for type-driven dispatch. Use `yield` only when a case needs multiple statements; otherwise the arrow alone is cleanest. Never mix arrow and colon styles in the same switch — compile error. Trust your IDE to convert legacy switch statements; it's usually a safe refactor that improves clarity. The deeper lesson: switch is no longer just a control structure — it's a first-class expression and the gateway to Java's modern pattern-matching family.
Switch Expressions in Java — Arrow Syntax, `yield`, Exhaustiveness
Direct Answer
Switch expressions (Java 14, JEP 361) make switch produce a value, eliminate fall-through bugs, and require exhaustive coverage. The new arrow syntax — case A -> result — evaluates one branch and returns a value; no break required. Multiple labels per case: case MONDAY, TUESDAY, WEDNESDAY -> "Weekday" replaces the old fall-through pattern. For multi-statement blocks, use yield to return a value from the block: case X -> { ... yield value; }. Switch expressions must be exhaustive — the compiler verifies all possible inputs are covered (no unhandled case, or a default). For enums, listing every constant satisfies exhaustiveness without default; for sealed types (Java 17+), listing every permitted subtype does the same. Old switch statements still work unchanged for backward compatibility, and you can use the arrow syntax in statements too. The deeper win is conceptual: switch becomes a first-class expression that can be used in return, in field initializers, and in method arguments — not just a control structure. This is the foundation that Java 21 pattern matching for switch (JEP 441) builds on, adding type patterns, record deconstruction, and when guards. Together they push Java toward algebraic-data-type-style matching seen in Scala and Kotlin.
Common Mistakes & Pitfalls
Using break with arrow syntax (not needed). Confusing yield with return. Not knowing exhaustive enum switches drop the default. Mixing arrow and colon cases in one switch (compile error)
Check your understanding
Answer these without looking back at the article.
- What's the difference between `yield` and `return` in a switch expression?
- Why doesn't an enum switch expression need `default`?
- Can you mix arrow and colon cases in one switch?
- How do switch expressions enable Java 21 pattern matching?
- Why should you remove `default` from enum switches when migrating?
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.