9 Switch Expressions
Like all expressions, switch
expressions evaluate to a
single value and can be used in statements. They may contain "case L ->
"
labels that eliminate the need for break
statements to prevent fall
through. You can use a yield
statement to specify the value of a switch
expression.
For background information about the design of switch
expressions, see
JEP 361.
"case L ->" Labels
Consider the following switch
statement that prints the
number of letters of a day of the week:
public enum Day { SUNDAY, MONDAY, TUESDAY,
WEDNESDAY, THURSDAY, FRIDAY, SATURDAY; }
// ...
int numLetters = 0;
Day day = Day.WEDNESDAY;
switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
numLetters = 6;
break;
case TUESDAY:
numLetters = 7;
break;
case THURSDAY:
case SATURDAY:
numLetters = 8;
break;
case WEDNESDAY:
numLetters = 9;
break;
default:
throw new IllegalStateException("Invalid day: " + day);
}
System.out.println(numLetters);
It would be better if you could "return" the length of the day's name
instead of storing it in the variable numLetters
; you can do this
with a switch
expression. Furthermore, it would be better if you
didn't need break
statements to prevent fall through; they are
laborious to write and easy to forget. You can do this with a new kind of
case
label. The following is a switch
expression that uses the new kind of case
label to print the number
of letters of a day of the week:
Day day = Day.WEDNESDAY;
System.out.println(
switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> 9;
default -> throw new IllegalStateException("Invalid day: " + day);
}
);
The new kind of case
label has the following form:
case label_1, label_2, ..., label_n -> expression;|throw-statement;|block
When the Java runtime matches any of the labels to the left of the arrow,
it runs the code to the right of the arrow and does not fall through; it does not
run any other code in the switch
expression (or statement). If the
code to the right of the arrow is an expression, then the value of that expression
is the value of the switch
expression.
You can use the new kind of case
label in
switch
statements. The following is like the first example,
except it uses "case L ->
" labels instead of "case
L:
" labels:
int numLetters = 0;
Day day = Day.WEDNESDAY;
switch (day) {
case MONDAY, FRIDAY, SUNDAY -> numLetters = 6;
case TUESDAY -> numLetters = 7;
case THURSDAY, SATURDAY -> numLetters = 8;
case WEDNESDAY -> numLetters = 9;
default -> throw new IllegalStateException("Invalid day: " + day);
};
System.out.println(numLetters);
A "case L ->
" label along with its code to its right is called a
switch labeled rule.
"case L:" Statements and the yield Statement
You can use "case L:
" labels in switch
expressions; a "case L:
" label along with its code to the right is
called a switch labeled statement group:
Day day = Day.WEDNESDAY;
int numLetters = switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
System.out.println(6);
yield 6;
case TUESDAY:
System.out.println(7);
yield 7;
case THURSDAY:
case SATURDAY:
System.out.println(8);
yield 8;
case WEDNESDAY:
System.out.println(9);
yield 9;
default:
throw new IllegalStateException("Invalid day: " + day);
};
System.out.println(numLetters);
The previous example uses yield
statements. They take
one argument, which is the value that the case
label produces in a
switch
expression.
The yield
statement makes it easier for you to
differentiate between switch
statements and switch
expressions. A switch
statement, but not a switch
expression, can be the target of a break
statement. Conversely, a
switch
expression, but not a switch
statement,
can be the target of a yield
statement.
Note:
It's recommended that you use "case L ->
" labels.
It's easy to forget to insert break
or yield
statements when using "case L:
" labels; if you do, you might
introduce unintentional fall through in your code.
For "case L ->
" labels, to specify multiple
statements or code that are not expressions or throw
statements, enclose them in a block. Specify the value that the
case
label produces with the yield
statement:
int numLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> {
System.out.println(6);
yield 6;
}
case TUESDAY -> {
System.out.println(7);
yield 7;
}
case THURSDAY, SATURDAY -> {
System.out.println(8);
yield 8;
}
case WEDNESDAY -> {
System.out.println(9);
yield 9;
}
default -> {
throw new IllegalStateException("Invalid day: " + day);
}
};
Exhaustiveness of switch Expressions
The cases of a switch
expression must be
exhaustive, which means that for all possible values, there must be a
matching switch label. Thus, a switch
expression normally requires
a default
clause. However, for an enum
switch
expression that covers all known constants, the compiler
inserts an implicit default
clause.
In addition, a switch
expression must either complete
normally with a value or complete abruptly by throwing an exception. For example,
the following code doesn't compile because the switch
labeled rule
doesn't contain a yield
statement:
int i = switch (day) {
case MONDAY -> {
System.out.println("Monday");
// ERROR! Block doesn't contain a yield statement
}
default -> 1;
};
The following example doesn't compile because the switch
labeled statement group doesn't contain a yield
statement:
i = switch (day) {
case MONDAY, TUESDAY, WEDNESDAY:
yield 0;
default:
System.out.println("Second half of the week");
// ERROR! Group doesn't contain a yield statement
};
Because a switch
expression must evaluate to a single value (or throw
an exception), you can't jump through a switch
expression with a
break
, yield
, return
, or
continue
statement, like in the following example:
z:
for (int i = 0; i < MAX_VALUE; ++i) {
int k = switch (e) {
case 0:
yield 1;
case 1:
yield 2;
default:
continue z;
// ERROR! Illegal jump through a switch expression
};
// ...
}
Exhaustiveness of switch Statements
The cases of a switch
statement must be exhaustive if it uses
pattern or null
labels, or if its selector expression isn't one of
the legacy types (char
, byte
,
short
, int
, Character
,
Byte
, Short
, Integer
,
String
, or an enum
type).
The following example doesn't compile because the switch
statement
(which uses pattern labels) is not exhaustive:
static void testSwitchStatementExhaustive(Object obj) {
switch (obj) { // error: the switch statement does not cover
// all possible input values
case String s:
System.out.println(s);
break;
case Integer i:
System.out.println("Integer");
break;
}
}
You can make it exhaustive by adding a default
clause:
static void testSwitchStatementExhaustive(Object obj) {
switch (obj) {
case String s:
System.out.println(s);
break;
case Integer i:
System.out.println("Integer");
break;
default:
break;
}
}