There has been a long trip with the Switch Expression on Java. It started as a preview feature in Java 12, receiving constant improvements on each newer version of Java. It was finally available in Java 17 (LTS) version, however on Java 21 (LTS) now it has newer features and changes from the features in Java 17.
This articule is based on the previous Switch Expression on Java 17, but highlighting those changes available now on Java 21. Let’s start…
Datatype Pattern Matching
This is the first step towards a more declarative and “null-safe” programming style, allowing a better way to express and model data, recognizing the semantics of the data model through patterns. Definitively improving the readability of our code, avoiding nested if/elseif/else
for many circumstances, and providing features that we see in other programming languages.
Let’s take the following code as reference, with some if/else if
and instanceof
.
static String formatter(Object o) { String formatted = "unknown"; if (o instanceof Integer i) { formatted = String.format("int %d", i); } else if (o instanceof Long l) { formatted = String.format("long %d", l); } else if (o instanceof Double d) { formatted = String.format("double %f", d); } else if (o instanceof String s) { formatted = String.format("String %s", s); } return formatted; }
The previous code can be converted with a switch expression.
static String formatterPatternSwitch(Object o) { return switch (o) { case Integer i -> String.format("int %d", i); case Long l -> String.format("long %d", l); case Double d -> String.format("double %f", d); case String s -> String.format("String %s", s); default -> o.toString(); }; }
Switch Expression vs Switch Statement
Now, how are the switch expression and statement going to co-exist? The first difference is in the operator used after the case
, the expression uses an arrow ->
while the statement continues using the operator :
(colon).
//Switch Expression example switch (o) { case Integer i -> String.format("int %d", i); case Long l -> String.format("long %d", l); } //Switch Statement example switch (o) { case Integer i: String.format("int %d", i); break; case Long l: String.format("long %d", l); break; }
It can be noticed that in the switch statement is still necessary to use the break
statement to terminate the switch flow, in other words, if break
is not indicated, it will continue executing the code blocks of the following labels. On the other hand, in the switch expression, only the code block of the matching pattern or value is executed.
It should be noted that both the sentence and the expression continue to work with exact or constant values, not only with patterns that it matches. For example, the following code is still valid.
//Switch statement switch (value) { case "A": callMethod1(); break; case "B": callMethod2(); break; default: callMethod3(); } //Switch Expression switch (value) { case "A"-> callMethod1(); case "B"-> callMethod2(); default -> callMethod3(); }
Support of null
Traditionally, the switch throws a NullPointerException
when the value is null
. This behavior is still present for existing switch statements, which do not make use of patterns. However, to make the new features of the switch more user-friendly (both the statement and the expression) allow null
as part of the pattern.
switch (o) { case null -> System.out.println("null!"); case String s -> System.out.println("String"); default -> System.out.println("Something else"); }
Guarded Pattern
This is a feature that I have seen in other languages and I think it was very successful that it is being added in Java as well. The “guarded pattern” is a condition that is part of an expression that must be matched and is preceded by the when
operator.
This is one of the changes from Java 17, where the operator for the guarded pattern was &&
. However, it was modified to avoid confusing developers with other uses of &&
.
switch(value) { case String s when (s.length > 3) -> System.out.println("A short string"); case String s when (s.length > 10) -> System.out.println("A medium string"); default -> System.out.println("A long string"); }
The Guarded Pattern can also be applied to Switch Statements, not only to Switch Expressions.
Returning values in block codes
Switch Expressions allows to return values, if after the arrow operator ->
the block of code is only one line, there is not need to use any “keyword”. It will return the result of that code line by default.
enum WeekDay {SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY;} String mood = switch (days) { case SUNDAY -> "Resting"; case MONDAY -> "Boring"; case TUESDAY -> "Go ahead"; case WEDNESDAY -> "Hump day"; case THURSDAY -> "Weekend is comming"; case FRIDAY -> "Weekend!!"; case SATURDAY -> "Yeah"; }
As you can see, the value is returning automatically, but if you have a block of code with multiple lines for any of those cases it requires a keyword at the end. In Java 17, that key was return
however it confused developers with the return
of the method, therefore for Java 21 that keyword is now yield
.
enum WeekDay {SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY;} String mood = switch (days) { case SUNDAY -> "Resting"; case MONDAY -> "Boring"; case TUESDAY -> "Go ahead"; case WEDNESDAY -> "Hump day"; case THURSDAY -> "Weekend is comming"; case FRIDAY -> { System.out.println("Need to do pending stuff"); yield "Weekend"; }; case SATURDAY -> "Yeah"; }
Take a look at the FRIDAY
, it has a block of code with more than one line, therefore when returning its value, it is using yield
keyword.
Multiple case Labels
Another nice feature to improve readability on the code. It’s possible to have case labels with multiple options.
enum WeekDay {SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY;} boolean gymDay = switch (days) { case MONDAY, TUESDAY, THURSDAY, FRIDAY -> true; default -> false; }
Instead of having multiple case labels with the same result, it is possible to have one case with multiple labels.
Conclusion
There are many other features of the switch that are not listed in this article, however, it is about showing the main ones, which I believe can provide a great improvement in usability to our code.
Definitely, Java as a language continues to evolve and provide features that will further simplify our code and keep it on par with many other new languages that are trending at the moment.
References
- https://openjdk.java.net/jeps/406
- https://www.infoworld.com/article/3606833/jdk-17-the-new-features-in-java-17.html
- https://docs.oracle.com/en/java/javase/21/language/pattern-matching-switch.html#GUID-E69EEA63-E204-41B4-AA7F-D58B26A3B232
- https://belief-driven-design.com/looking-at-java-21-switch-pattern-matching-14648/
- https://blog.adamgamboa.dev/switch-expression-on-java-17/