Pattern Dominance in switch
Pattern dominance is the compile-time rule that prevents a `switch` label from being unreachable because an earlier label already matches every value it could match. In Java pattern switches, case order therefore matters: broader patterns must not precede narrower patterns unless the broader case is appropriately guarded.
A pattern dominates another pattern when every value matched by the later pattern is also matched by the earlier one. The compiler rejects the later label because execution could never reach it. This is especially important when a broad type pattern appears before a subtype pattern.
For example, `case CharSequence cs` matches strings, so placing `case String s` after it is invalid: every `String` is already handled by the first case. Reversing the cases makes both labels reachable.
Dominance also applies to related pattern forms. An unconditional pattern can dominate a later pattern whose matched values are a subset of the earlier pattern. Guards can affect reachability; a guarded pattern is generally less restrictive for dominance analysis than an unconditional pattern, while a guard known to be `true` can behave as unconditional.
The rule is primarily about the static ordering of labels, not about which case would be desirable at runtime. When designing a pattern switch, put more specific patterns before more general ones and leave an unconditional pattern such as `case Object o` or a total pattern for the end when it is needed.
Key points
- A case is dominated when an earlier case already matches all of its possible values.
- A supertype pattern can dominate a subtype pattern, so specific cases normally come first.
- An unconditional pattern near the top can make every later compatible pattern unreachable.
- Guards can preserve reachability when the earlier pattern is not unconditionally applicable.
- Pattern dominance is checked by the compiler and results in a compile-time error.
Interview questions
What does pattern dominance mean in a Java switch?
A pattern dominates a later pattern when the earlier pattern already matches every value the later pattern could match. The later case would therefore be unreachable. Java reports this as a compile-time error. The practical rule is to place more specific patterns before broader ones.
Why does `case CharSequence cs` before `case String s` fail?
`String` implements `CharSequence`, so every value that matches `case String s` also matches `case CharSequence cs`. Because the first case would handle every possible `String`, the later `String` case could never execute. The compiler consequently rejects the ordering. Reversing the two cases makes both reachable.
Does a guarded pattern always dominate an unguarded pattern of the same type?
No. A guarded pattern does not generally match every value of its underlying type because its guard may reject some values. Therefore it does not normally make an equivalent unguarded pattern unreachable. A guard that is known to be `true` is a special case because it can be treated as unconditional for dominance analysis.
How would you order several related type patterns?
I would order them from the most specific type to the most general type. For example, a `String` case should precede a `CharSequence` case, which should precede an `Object` case if all are needed. This prevents broader patterns from dominating narrower ones. I would also keep any unconditional fallback pattern at the end.
How is dominance different from exhaustiveness in pattern matching?
Dominance is a local ordering property between switch labels: it determines whether a later label has any values left to handle after earlier labels are considered. Exhaustiveness is a property of the complete switch: it determines whether every permitted input is covered. A switch can be exhaustive while still containing a dominance error if one label is unreachable. Conversely, a switch can have no dominance error but still be non-exhaustive.