Switch Expressions
Switch Expressions
Java has had the switch statement since version 1.0. For decades it worked, but it came with sharp edges: forgotten breaks caused silent fall-through bugs, you could not use switch where a value was required, and the scope rules for local variables were surprising. Java 14 made switch expressions a permanent feature, and the enhancements continued through Java 17 and 21. This lesson covers the modern form thoroughly.
The Problem With the Classic switch Statement
Consider a method that maps a day-of-week to a category:
Three problems are visible here: (1) the repetitive case X: labels, (2) the mandatory break to prevent fall-through, and (3) the need for a mutable result variable. Switch expressions fix all three.
Arrow-Case Syntax: Switch as an Expression
The arrow form uses -> instead of :. Each arm is a standalone rule — there is no fall-through, and no break is needed.
Notice several things at once:
- Multiple labels on one
casearm are separated by commas — no stacking of identicalcase X:lines. - The whole
switchproduces a value, assigned directly byreturn. - No
defaultis needed here becauseDayOfWeekis an enum and all seven values are covered. The compiler verifies this at compile time — exhaustiveness is enforced.
Returning Complex Values: the yield Keyword
The right-hand side of an arrow arm can be a single expression, but sometimes you need more logic — a local variable, an if, or a loop. In that case you use a block body and the yield keyword to produce the switch value:
yield is not the same as return. return exits the enclosing method; yield exits only the switch block and hands its value to the switch expression. You cannot use return inside a switch block to supply the expression value.
Mixing Arrow Arms With a Default
For non-enum selectors — String, int, or arbitrary objects in Java 21+ pattern switches — exhaustiveness cannot always be verified at compile time, so a default arm is required:
yield (not break) to produce an expression value — another reason to prefer arrow arms exclusively in new code.
Assigning the Result to a Variable
A switch expression is just an expression. Anywhere a value is valid, a switch expression is valid — assignment, method argument, ternary operand, or return:
Pattern Matching in Switch (Java 21)
Java 21 extended switch expressions with type patterns (preview in 17, finalised in 21). You can now match on the runtime type of an object:
The pattern variable (i, d, s) is scoped to that arm only and is already cast — no explicit cast required, no ClassCastException possible. The null case can now be handled inside the switch rather than with a pre-check, and exhaustiveness is still enforced when matching sealed hierarchies.
Trade-offs and When to Use Each Form
- Use arrow-case switch expressions whenever you need to map a value to a result. This is the majority of switch use-cases.
- Use
yieldonly when an arm genuinely needs multi-line logic that cannot be extracted to a helper. - Avoid the colon form in new code. The only reason to keep it is when intentional fall-through is the clearest way to express a rule — which is rare.
- Prefer switch expressions over long if-else chains on a single variable. The compiler can check exhaustiveness; an if-else chain cannot.
Summary
Switch expressions transform switch from a control-flow statement into a value-producing construct. The arrow syntax eliminates fall-through and removes the need for break. Multiple labels on a single arm replace stacked case lines. yield lets block arms produce a value without exiting the method. Exhaustiveness is enforced at compile time for enums and sealed types. Together these features make switch both safer and more expressive — a first-class tool for mapping and dispatching, not just a legacy control-flow mechanism.