What is a guarded pattern (the `when` clause) in a switch, and how does it interact with case ordering and exhaustiveness?
answer
- case Pattern when <boolean> -> ...
- False guard = fall through to next case
- Guarded cases do NOT satisfy exhaustiveness
- Specific guards before the unguarded catch-all (dominance)
- `when` is a contextual keyword
basics
~20 sA guarded pattern adds a boolean condition to a case using when, e.g. case Integer i when i > 0. The case matches only if the value fits the pattern AND the condition is true. If the guard is false, the switch keeps trying later cases.
solid answer
~50 sA guard refines a pattern with an extra boolean test written after `when`. The case matches only when both the pattern matches and the guard expression (which can use the bound variable) evaluates to true; otherwise the switch falls through to subsequent cases. Because a false guard means "no match here," you almost always need a follow-up case for the same type without a guard (or a default) so the input is still covered. Critically, a guarded case does **not** count toward exhaustiveness for that type — the compiler can't prove the guard is always true, so a guarded `case Integer i when ...` alone won't make the switch exhaustive for integers. Order matters: put the more specific guarded cases before the broader catch-all of the same type, since the first matching case wins. Guards keep conditional logic readable inside the switch instead of nesting `if` statements in each branch.
code
java · 8 linesstatic String classify(Object o) {
return switch (o) {
case Integer i when i < 0 -> "negative";
case Integer i when i == 0 -> "zero";
case Integer i -> "positive"; // unguarded fallback
default -> "not an int";
};
}go deeper
Knows when adds a condition and the case matches only if both pattern and condition hold.
Orders guarded cases correctly, knows a false guard falls through, and that guarded cases don't satisfy exhaustiveness so a fallback is needed.
Explains dominance errors, exhaustiveness reasoning, contextual-keyword nature of when, and advises against side-effecting guards.
Reasons about guards vs. extracting predicates into well-named methods, readability/maintainability trade-offs, and how guards fit data-oriented control flow over sealed hierarchies.
## Recap: type patterns A `switch` picks a branch by matching the *selector* value. A **type pattern** like `case Integer i` matches when the selector is an instance of `Integer` and binds it to `i`. (See the type-patterns question.) ## What a guard adds A **guarded pattern** attaches an extra **boolean condition** to a pattern using the contextual keyword `when`. Syntax: ```java case Integer i when i > 100 -> "big"; ``` The case matches **only if both** are true: 1. the pattern matches (the selector is an `Integer`, bound to `i`), **and** 2. the **guard** — the expression after `when`, here `i > 100` — evaluates to `true`. The guard may freely use the pattern's bound variable. If the pattern matches but the guard is `false`, this case is treated as **not matched**, and the switch continues evaluating the **following** cases in order. > `when` is a *contextual* keyword: it only has this meaning right after a pattern in a case label, so it does not break existing code that uses `when` as an identifier. ## Why ordering matters Cases are tested **top to bottom; the first match wins.** So specific guarded cases must come **before** a broader unguarded case of the same shape: ```java String classify(Object o) { return switch (o) { case Integer i when i < 0 -> "negative"; case Integer i when i == 0 -> "zero"; case Integer i -> "positive"; // catch-all for the rest default -> "not an int"; }; } ``` If you placed the unguarded `case Integer i` first, the guarded ones below it would be unreachable — and the compiler will reject a case that is **dominated** (made unreachable) by an earlier one. ## Guards and exhaustiveness **Exhaustiveness** means the switch must handle every possible input (required for pattern switches and all switch expressions). A **guarded** case does **not** contribute to exhaustiveness, because the compiler cannot prove the boolean guard is always true. Therefore: ```java // NOT exhaustive — only covers integers whose guard is true switch (o) { case Integer i when i > 0 -> ...; } ``` This fails to compile (for an expression or pattern switch) unless you add an **unguarded** fallback for the type and/or a `default`. The mental rule: *a guard can never be the thing that "closes off" a type — you still need an unconditional case or default.* ## Guard expression rules - The guard is any boolean expression; it can call methods, combine conditions with `&&`/`||`, and reference the bound variable(s). - Keep guards **side-effect-free**; a guard that mutates state or has costly side effects makes control flow hard to reason about. - A guard can throw — if it does, the switch propagates the exception. ## Deriving an answer Guard = pattern + boolean test. False guard = skip to next case. Because the compiler can't trust a guard to always hold, guarded cases never make a switch exhaustive, so you always pair them with an unguarded case or default; and because first-match-wins, specific guarded cases go above the general one.
- Why won't `switch(o){ case Integer i when i>0 -> ...; }` compile as a switch expression over Object?It is not exhaustive: the guarded case only covers positive integers, and guards never count toward exhaustiveness. You need an unguarded `case Integer i` and/or a `default` to cover the remaining inputs.
- What happens if a guarded case is placed after an unguarded case of the same type?The earlier unguarded case dominates it — every value of that type is already caught — so the guarded case is unreachable and the compiler reports a dominance error.
saying these in an interview costs you the question
- Believing a guarded case alone makes the switch exhaustive for that type
- Putting the unguarded case before guarded ones (causes dominance/unreachable error)
- Thinking a false guard throws or jumps to default instead of trying the next case
- Using side-effecting guards and assuming predictable evaluation