Java switch return value
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.
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.
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); >>;
Unlike switch statements, the cases of switch expressions must be exhaustive , which means that for all possible values, there must be a matching switch label. Thus, switch expressions normally require a default clause. However, for enum switch expressions that cover 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:
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:
Branching with Switch Expressions
In Java SE 14 you can use another, more convenient syntax for the switch keyword: the switch expression.
Several things have motivated this new syntax.
- The default control flow behavior between switch labels is to fall through. This syntax is error-prone and leads to bugs in applications.
- The switch block is treated as one block. This may be an impediment in the case where you need to define a variable only in one particular case .
- The switch statement is a statement. In the examples of the previous sections, a variable is given a value in each case . Making it an expression could lead to better and more readable code.
The syntax covered in the previous section, known as switch statement is still available in Java SE 14 and its semantics did not change. Starting with Java SE 14 a new syntax for the switch is available: the switch expression.
This syntax modifies the syntax of the switch label. Suppose you have the following switch statement in your application.
int day = . ; // any day int len = 0; switch (day) < case MONDAY: case FRIDAY: case SUNDAY: len = 6; break; case TUESDAY: len = 7; break; case THURSDAY: case SATURDAY: len = 8; break; case WEDNESDAY: len = 9; break; >System.out.println("len = " + len);
With the switch expression syntax, you can now write it in the following way.
int day = . ; // any day int len = switch (day) < case MONDAY, FRIDAY, SUNDAY ->6; case TUESDAY -> 7; case THURSDAY, SATURDAY -> 8; case WEDNESDAY -> 9; > System.out.println("len = " + len);
The syntax of switch label is now case L -> . Only the code to the right of the label is executed if the label is matched. This code may be a single expression, a block, or a throw statement. Because this code is one block, you can define variables in it that are local to this particular block.
This syntax also supports multiple constants per case, separated by commas, as shown on the previous example.
Producing a Value
This switch statement can be used as an expression. For instance, the example of the previous section can be rewritten with a switch statement in the following way.
int quarter = . ; // any value String quarterLabel = switch (quarter) < case 0 ->"Q1 - Winter"; case 1 -> "Q2 - Spring"; case 2 -> "Q3 - Summer"; case 3 -> "Q3 - Summer"; default -> "Unknown quarter"; >;
If there is only one statement in the case block, the value produced by this statement is returned by the switch expression.
The syntax in the case of a block of code is a little different. Traditionally, the return keyword is used to denote the value produced by a block of code. Unfortunately this syntax leads to ambiguity in the case of the switch statement. Let us consider the following example. This code does not compile, it is just there as an example.
// Be careful, this code does NOT compile! public String convertToLabel(int quarter) < String quarterLabel = switch (quarter) < case 0 ->< System.out.println("Q1 - Winter"); return "Q1 - Winter"; >; default -> "Unknown quarter"; >; > return quarterLabel; >
The block of code executed in the case where quarter is equal to 0 needs to return a value. It uses the return keyword to denote this value. If you take a close look at this code, you see that there are two return statements: one in the case block, and another one in the method block. This is where the ambiguity lies: one may be wondering what is the semantics of the first return . Does it mean that the program exits the method with this value? Or does it leave the switch statement? Such ambiguities lead to poor readability and error-prone code.
A new syntax has been created to solve this ambiguity: the yield statement. The code of the previous example should be written in the following way.
public String convertToLabel(int quarter) < String quarterLabel = switch (quarter) < case 0 ->< System.out.println("Q1 - Winter"); yield "Q1 - Winter"; >; default -> "Unknown quarter"; >; > return quarterLabel; >
The yield statement is a statement that can be used in any case block of a switch statement. It comes with a value, that becomes the value of the enclosing switch statement.
Adding a Default Clause
Default clauses allow your code to handle cases where the selector value does not match any case constant.
The cases of a switch expression must be exhaustive. For all possible values, there must be a matching switch label. Switch statements are not required to be exhaustive. If the selector target does not match any switch label, this switch statement will not do anything, silently. This may be a place for bugs to hide in your application, something you want to avoid.
In most of the cases, exhaustiveness can be achieved using a default clause; however, in the case of an enum switch expression that covers all known constants, you do not need to add this default clause.
There is still a case that needs to be dealt with. What would happen if someone adds an enumerated value in an enumeration, but forget to update the switch statements on this enumeration? To handle this case, the compiler adds a default clause for you in exhaustive switch statements. This default clause will never be executed in normal cases. It will be only if an enumerated value has been added, and will throw an IncompatibleClassChangeError .
Handling exhaustiveness is a feature of switch expressions that is not provided by traditional switch statements and that is used in other cases than switch on enumerated values.
Writing Colon Case in Switch Expressions
A switch expression can also use a traditional case block with case L: . In this case the fall through semantics does apply. Values are yielded using the yield statement.
int quarter = . ; // any value String quarterLabel = switch (quarter) < case 0 : yield "Q1 - Winter"; case 1 : yield "Q2 - Spring"; case 2 : yield "Q3 - Summer"; case 3 : yield "Q3 - Summer"; default: System.out.println("Unknown quarter"); yield "Unknown quarter"; >;
Dealing with Null Values
So far, switch statements do not accept null selector values. If you try to switch on a null value you will get a NullPointerException .
Java SE 17 has a preview feature that enhances switch expressions to allow for null values, so you can expect this situation to change.