Java 12 introduced switch expressions which improved the way we can write switch-case statements.
Over the next few versions, the switch-case statement has been improved several times. Let's take a look at the improvements and compare them to the original switch-case statement.
1. Arrow Case Expression
Earlier, case blocks were required to end with a break statement. Otherwise, there would be a fall-through problem.
// OLD
switch (x) {
case 1:
System.out.println("One");
break;
case 2:
System.out.println("Two");
break;
default:
System.out.println("Unknown");
}
Now, we can use an arrow case expression to simplify the case block. Only the statement appearing after the arrow will be executed if the case matches.
// NEW
switch (x) {
case 1 -> System.out.println("One");
case 2 -> System.out.println("Two");
default -> throw new IllegalArgumentException();
}
This is similar to lambda syntax. If there were multiple statements, we could use a block instead.
// NEW (with block)
switch (x) {
case 1 -> {
System.out.println("One");
System.out.println("One again");
}
case 2 -> System.out.println("Two");
default -> System.out.println("Unknown");
}
2. Multiple matching values
What if a fallthrough was desired? In the old ways, we would have to write a separate case for each value and let it fall through to the last case which will contain the operations.
// OLD (fallthrough)
switch (x) {
case 1:
case 2:
System.out.println("Valid values");
break;
default:
System.out.println("Unknown");
}
In the improved version, this can be done with a comma separated list of values.
// NEW (multiple values)
switch (x) {
case 1, 2:
System.out.println("Valid values");
default:
System.out.println("Unknown");
}
3. Switch expressions
A big improvement is that switch statements can be used to return a value and therefore can be used as expressiond.
For example, we can use a switch-case block inline to print a value in words.
// switch expressions
System.out.println(switch (x) {
case 1 -> "One";
case 2 -> "Two";
default -> "Unknown";
});
4. The yield keyword
When case has a single statement, as seen above, the value returned by the statement is returned by the switch expression.
If case has a block, we need to explicitly return the value using the yield keyword.
// switch expressions with yield
String word = switch (x) {
case 1 -> {
doSomething();
yield "One";
}
case 2 -> "Two";
default -> "Unknown";
};
Old-style case blocks can also be used in switch expressions and can have a yield statement.
When using a yield statement, a break statement is not required.
This is not recommended though. It's best to stick to the new style in order to avoid confusion and unexpected mistakes.
// old-style case blocks with yield
String word = switch (x) {
case 1:
doSomething();
yield "One";
case 2:
yield "Two";
default:
yield "Unknown";
};
5. Exhaustiveness of switch expressions
Switch case statements are not required to be exhaustive. It is fine if no case matches and there is no default case.
However, when using switch to return a value, it is required that all cases are covered and one of them should always match.
For e.g., when implementing a switch on numbers, the default case cannot be left out:
// missing default case
String word = switch (x) {
case 1 -> "One";
case 2 -> "Two";
};
/* gives the error:
error: the switch expression does not cover all possible input values
String word = switch (x) {
^
*/
This means, we will always need to have a default case unless working with booleans.
The only exception to this is while using an enum and covering all possible enum values.
The below code works fine but it will give the same error as above if we remove one of the cases.
// switch expressions with enum
public class MyClass {
static enum Color { RED, GREEN, BLUE };
public static void main(String[] args) {
Color color = Color.RED;
System.out.println(switch (color) {
case RED -> "Red";
case GREEN -> "Green";
case BLUE -> "Blue";
});
}
}
That's it for the improvements. One more feature worth mentioning is pattern matching but I have not covered it because it is still a preview feature.
An important point to note is that having an improved syntax does not mean the old syntax is invalid now. Both syntaxes work.
Thank you for reading. If you want to connect with me, you can find me on Twitter where I post regular Java tips and quizzes.
Latest comments (2)
Exhaustive cases (default) are needed if you want to write expressions even for boolean case values.
break / yield is needed even if you have multiple values for each case when arrow is not used.
Thanks, a great explanation about the modern switch in java!