switch chooses what to run from the value of a selector
expression. It has been a statement since Java 1.0 and can also be an expression
since Java 14; since Java 21 its labels can be patterns.
Both forms share the same switch block rules (JLS 14.11 and 15.28).
Grammar
SwitchStatement:
switch ( Expression ) SwitchBlock
SwitchExpression:
switch ( Expression ) SwitchBlock
SwitchBlock:
{ SwitchRule {SwitchRule} }
{ {SwitchBlockStatementGroup} {SwitchLabel :} }
SwitchRule:
SwitchLabel -> Expression ;
SwitchLabel -> Block
SwitchLabel -> ThrowStatement
SwitchBlockStatementGroup:
SwitchLabel : {SwitchLabel :} BlockStatements
SwitchLabel:
case CaseConstant {, CaseConstant}
case null [, default]
case CasePattern {, CasePattern} [Guard]
default
Guard:
when Expression
A switch block uses either arrow rules or colon groups, never both.
Selector types
| Selector type | Labels allowed | Since |
|---|---|---|
char, byte, short, int | Constant expressions assignable to the type | 1.0 |
Character, Byte, Short, Integer | The same constants (the selector is unboxed) | 5 |
| An enum type | Enum constant names; qualified names too since 21 | 5 |
String | String constants, compared with equals | 7 |
| Any other reference type | Patterns, null, default; and enum constants when they belong to the selector’s type | 21 |
boolean, long, float, double | Not allowed. Primitive patterns in Java 23 to 27, a preview feature, allow them | preview |
Case constants must be distinct, and must be constant expressions, so a
final local variable with a constant initialiser can be a label but a
method call cannot.
Colon groups and fall-through
With case L: labels, execution jumps to the matching label and carries
on through the following groups until a break or the end of the block.
This fall-through is a common source of mistakes; javac
-Xlint:fallthrough warns about it.
switch (month) {
case 4: case 6: case 9: case 11:
days = 30;
break;
case 2:
days = leap ? 29 : 28;
break;
default:
days = 31;
}
All groups share one scope, so a variable declared in one group is in scope in the
later ones (though not definitely assigned there). Since Java 14 a label may list
several constants: case 4, 6, 9, 11:.
Arrow rules
With case L -> (Java 14), only the matching rule runs: there is no
fall-through and no break is needed. The right side is an expression, a
block or a throw statement, and each rule has its own scope.
switch (command) {
case "start", "go" -> engine.start();
case "stop" -> engine.stop();
case "status" -> {
var s = engine.status();
System.out.println(s);
}
default -> throw new IllegalArgumentException(command);
}
Switch expressions
A switch expression (Java 14, JEP 361) has a value. With arrow rules, the value of
an expression rule is that expression; a block must end in yield or
throw. With colon groups, values are given with yield only.
int days = switch (month) {
case 4, 6, 9, 11 -> 30;
case 2 -> {
int d = leap ? 29 : 28;
yield d;
}
default -> 31;
};
String size = switch (n) {
case 0: yield "none";
case 1: yield "one";
default: yield "many";
};
- A switch expression must be exhaustive: some label must apply to every possible value.
- Every rule or group must give a value or throw: completing normally without a
value is an error.
break,continueandreturncannot leave a switch expression. - In an assignment or invocation context, a switch expression is a poly expression:
each result is checked against the target type. Used standalone, its type follows
the rules of the conditional
operator:
booleanif all results are boolean, numeric promotion if all are numeric, otherwise the boxed results’ least upper bound. - A switch expression cannot be an expression statement: a statement that starts
with
switchis always the switch statement.
The yield statement
YieldStatement:
yield Expression ;
yield leaves the innermost switch expression with a value. It is a
contextual keyword: yield is a statement only where a statement starting
with the identifier yield could not otherwise be valid. A method called
yield must therefore be called qualified, Thread.yield(),
and no type may be named yield.
case null
A switch whose selector is null throws
NullPointerException, unless it has a case null label
(Java 21). default does not match null; to send
null to the default code, write case null, default.
String label = switch (obj) {
case null -> "nothing";
case String s -> "text: " + s;
case Integer i -> "number: " + i;
default -> "other";
};
Pattern labels and guards
Since Java 21 (JEP 441) a case label can be a type pattern or a record pattern, with
an optional guard after when. The labels are tried in order; the first
whose pattern matches and whose guard is true applies.
static String classify(Object o) {
return switch (o) {
case Integer i when i > 0 -> "positive int";
case Integer i -> "other int";
case String s when s.isEmpty() -> "empty string";
case String s -> "string of " + s.length();
case Point(int x, int y) when x == y -> "diagonal point";
case Point _ -> "point";
case int[] a -> "int array of " + a.length;
default -> "something else";
};
}
- A guard is a
booleanexpression. It may use the pattern’s variables, and local variables of the enclosing code only if they are final or effectively final. A guard that is the constantfalseis an error. - One label may list several patterns only if none of them declares a variable
(Java 22):
case Integer _, Long _ ->. - In colon groups, a label that declares pattern variables cannot be reached by fall-through, and cannot share its statements with another label.
Dominance and order
A label that can never apply because an earlier one always applies first is dominated, which is an error (JLS 14.11.1):
- A pattern label is dominated by an earlier unguarded label whose pattern matches
everything it matches:
case CharSequence csbeforecase String sis an error. - A guarded label is dominated by an earlier label with the same pattern and no guard; a guarded label does not dominate anything.
- A constant label is dominated by an earlier pattern label that covers it:
case Integer ibeforecase 42is an error. Put constants first. defaultandcase null, defaultare dominated by an earlier unguarded pattern that matches every value of the selector type.- A
defaultlabel may not come before a label with a pattern or withnull, andcase null, defaultmust be last. For old code,defaultmay still come before plain constant labels.
Exhaustiveness
A switch block is exhaustive for its selector if it has a
default or case null, default label, or if its unguarded
constants and patterns together cover the selector’s type (JLS 14.11.1.1):
| Selector type | Covered by |
|---|---|
Any type T | An unguarded type pattern of T or a supertype |
| An enum | All of its constants |
| A sealed class or interface | Patterns covering each permitted subtype (recursively for sealed subtypes); an abstract sealed class need not be covered itself |
| A record type | Record patterns whose component patterns together cover each component, for example Pair(A a, X x) and Pair(B b, X x) for a sealed A | B |
Which switches must be exhaustive:
- every switch expression;
- every enhanced switch statement: one whose selector type is not
char,byte,short,int, their wrappers,Stringor an enum, or that uses a pattern ornulllabel; - older forms of switch statement need not be exhaustive, for compatibility.
Coverage is checked at compile time against the types as they are then. If at run
time no label applies, for example because a new subclass or enum constant was added
and the switch was not recompiled, an exhaustive enhanced switch throws
MatchException.
At run time
- The selector is evaluated, and unboxed if the labels are primitive constants.
- If it is
null:case nullapplies if present; otherwiseNullPointerExceptionis thrown. - Otherwise each label is tried in order: constants by
==(byequalsfor strings, by identity for enum constants), patterns by matching, then the guard. The first label that applies is taken. - If none applies,
defaultis taken; without one, an older switch statement does nothing and an exhaustive switch throwsMatchException.