Java Switch Expressions For Impatiens
This is the super-fast guide of Java 12/13 switch expressions. Learn how to use switch expressions in 5 minutes.
Join the DZone community and get the full member experience.
Join For FreeThis is the super-fast guide of Java 12/13 switch
expressions. Learn how to use switch
expressions in 5 minutes.
You may also like: JDK 12: Switch Statements/Expressions in Action
Old-School Switch
Before we have a brief overview of the switch
expressions introduced in JDK 12, let's see a typical old-school example wrapped in a method (PlayerTypes
is a simple Java enum
).
private static Player createPlayer(PlayerTypes playerType) {
switch (playerType) {
case TENNIS:
return new TennisPlayer();
case FOOTBALL:
return new FootballPlayer();
case SNOOKER:
return new SnookerPlayer();
case UNKNOWN:
throw new UnknownPlayerException("Player type is unknown");
default:
throw new IllegalArgumentException(
"Invalid player type: " + playerType);
}
}
If we forget about
default
, then the code will not compile.
The preceding example is acceptable. In the worst-case scenario, we can add a spurious variable (for example, player
), some cluttering break
statements, and get no complaints if default
is missing. So, the following code is an old-school, extremely ugly switch.
xxxxxxxxxx
private static Player createPlayerSwitch(PlayerTypes playerType) {
Player player = null;
switch (playerType) {
case TENNIS:
player = new TennisPlayer();
break;
case FOOTBALL:
player = new FootballPlayer();
break;
case SNOOKER:
player = new SnookerPlayer();
break;
case UNKNOWN:
throw new UnknownPlayerException(
"Player type is unknown");
default:
throw new IllegalArgumentException(
"Invalid player type: " + playerType);
}
return player;
}
If we forget about default
, then there will be no complaints from the compiler side. In this case, a missing default
case may result in a null
player.
Java 12 Switch Expression
However, since JDK 12, we have been able to rely on the switch
expressions. Before JDK 12, switch
was a statement, a construct meant to control the flow (for example, like an if
statement) without representing the result. On the other hand, an expression is evaluated to a result. Therefore, a switch
expression can have a result.
The preceding old-school switch
expression can be written in the style of JDK 12 as follows.
xxxxxxxxxx
private static Player createPlayer(PlayerTypes playerType) {
return switch (playerType) {
case TENNIS -> new TennisPlayer();
case FOOTBALL -> new FootballPlayer();
case SNOOKER -> new SnookerPlayer();
case UNKNOWN -> throw new UnknownPlayerException(
"Player type is unknown");
// default is not mandatory
default -> throw new IllegalArgumentException(
"Invalid player type: " + playerType);
};
}
This time,
default
is not mandatory. We can skip it.
The JDK 12 switch
is smart enough to signal if switch
doesn't cover all possible input values. This is very useful in the case of Java enum
values. The JDK 12 switch
can detect whether all the enum values are covered, and don't force a useless default
if they aren't. For example, if we remove default
and add a new entry to PlayerTypes
enum (for example, GOLF
), then the compiler will signal it via a message, as in the following screenshot (this is from NetBeans).
Notice that between the label and execution, we've replaced the colon with an arrow (the lambda-style syntax). The main role of this arrow is to prevent fall-through, which means that only the block of code from its right will be executed. There is no need to use break
.
Do not conclude that the arrow turns the switch
statement into a switch
expression. A switch
expression can be used with a colon and break
as well, as follows:
xxxxxxxxxx
private static Player createPlayer(PlayerTypes playerType) {
return switch (playerType) {
case TENNIS: break new TennisPlayer();
case FOOTBALL: break new FootballPlayer();
case SNOOKER: break new SnookerPlayer();
case UNKNOWN: throw new UnknownPlayerException(
"Player type is unknown");
// default is not mandatory
default: throw new IllegalArgumentException(
"Invalid player type: " + playerType);
};
}
Our example posts
switch
overenum
, but the JDK 12switch
can also be used overint
,Integer
,short
,Short
,byte
,Byte
,char
,Character
, andString
.Notice that JDK 12 brings the
switch
expressions as a preview feature. This means that it is prone to changes in the next few releases, and it needs to be unlocked via the--enablepreview command-line
option at compiling and runtime.
The complete example is available on GitHub.
Multiple Case Labels
Before JDK 12, a switch
statement allowed a single label per case
. Starting with the switch
expressions, a case
can have multiple labels separated by a comma. Check out the following method that exemplifies multiple case
labels:
xxxxxxxxxx
private static SportType fetchSportTypeByPlayerType(PlayerTypes playerType) {
return switch (playerType) {
case TENNIS, GOLF, SNOOKER -> new Individual();
case FOOTBALL, VOLLEY -> new Team();
};
}
If we pass to this method TENNIS
, GOLF
, or SNOOKER
, it will return an instance of the Individual
class. If we pass FOOTBALL
or VOLLEY
, it will return an instance of the Team
class.
The complete example is available on GitHub.
Statement Blocks
A label's arrow can point to a single statement (as in the examples from the previous two examples) or a curly-braced block. This is pretty similar to the lambda blocks. Check out the following solution:
xxxxxxxxxx
private static Player createPlayer(PlayerTypes playerType) {
return switch (playerType) {
case TENNIS -> {
System.out.println("Creating a TennisPlayer ...");
break new TennisPlayer();
}
case FOOTBALL -> {
System.out.println("Creating a FootballPlayer ...");
break new FootballPlayer();
}
case SNOOKER -> {
System.out.println("Creating a SnookerPlayer ...");
break new SnookerPlayer();
}
default -> throw new IllegalArgumentException(
"Invalid player type: " + playerType);
};
}
Notice that we exit from a curly-braced block via break
, not return
. In other words, while we can return
from inside a switch
statement, we can't return
from within an expression.
The complete example is available on GitHub.
Java 13 Switch Expressions
However, since JDK 13, the break
with a value statement is dropped in favor of a yield
statement. The rest remains the same. So, using yield
we obtain:
xxxxxxxxxx
private static Player createPlayer(PlayerTypes playerType) {
return switch (playerType) {
case TENNIS: yield new TennisPlayer();
case FOOTBALL: yield new FootballPlayer();
case SNOOKER: yield new SnookerPlayer();
case UNKNOWN: throw new UnknownPlayerException(
"Player type is unknown");
// default is not mandatory
default: throw new IllegalArgumentException(
"Invalid player type: " + playerType);
};
}
The complete example is available on GitHub.
We also obtain:
xxxxxxxxxx
private static Player createPlayer(PlayerTypes playerType) {
return switch (playerType) {
case TENNIS -> {
System.out.println("Creating a TennisPlayer ...");
yield new TennisPlayer();
}
case FOOTBALL -> {
System.out.println("Creating a FootballPlayer ...");
yield new FootballPlayer();
}
case SNOOKER -> {
System.out.println("Creating a SnookerPlayer ...");
yield new SnookerPlayer();
}
default -> throw new IllegalArgumentException(
"Invalid player type: " + playerType);
};
}
In a nutshell, the arrow does not signify an expression instead of a statement. The arrow-form prevents fall-through.
Labels with a colon merely mark an entry point into an execution. From there it continues, even when it passes another label. In
switch
, we know this as fall-through. In other words, acase
label determines where the control flow jumps to, but it needs abreak
,yield
orreturn
to quit flowing through theswitch
.On the other hand, the arrow-form signifies that only the block to its right will be executed. That’s right, no fall-through!
Done! The complete example is available on GitHub.
If you enjoyed this article, then you'll love my book, Java Coding Problems, that entirely dedicated to JDK 8 to JDK 13 features.
Happy coding!
Further Reading
Opinions expressed by DZone contributors are their own.
Comments