The New Switch (Switch Expressions)
Learn how to use the new switch, and say goodbye to the old construct!
Join the DZone community and get the full member experience.Join For Free
According to some surveys such as that of JetBrains, version 8 of Java is currently the most used by developers all over the world, despite being a 2014 release.
What you are reading is an article in a series titled 'Going beyond Java 8', inspired by the contents of my book 'Java for Aliens'. These articles will guide the reader step by step to explore the most important features introduced starting from version 9. The aim is to make the reader aware of how important it is to move forward from Java 8, explaining the enormous advantages that the latest versions of the language offer.
In this article we will see how Oracle with Java 14 has officially introduced an evolution of the
switch construct, which can now also be used as an expression, that is, it can return a value. This novelty is known as the
switch expression. However, the introduction of
switch expressions also caused the modification of the original construct, with the definition of a new word in the Java vocabulary (
yield), and the possibility of using the arrow notation in this construct, which was introduced with the lambda expressions with Java 8. We will therefore distinguish the
switch construct when used as a statement and as an expression, but rather than talking only about
switch expressions, it seems more appropriate to talk about a renewed construct: the new
switch, which can now be used as a statement or as expression.
To get a clearer picture of the situation, we also briefly describe the history of the construct (if you are not interested you can also skip the next section).
switch Has Been Updated
switch construct, like all Java constructs and the ternary operator, has been inherited from the C language since the first version of Java (an exception is the enhanced for loop that was introduced with Java 5).
This construct has characteristics that are well suited to the creation of typical programs that were once developed with this language, for example, parsers and binary coders. Within Java programming, however, it has always been seen by programmers as a secondary construct. With the advent of enumerations in Java,
switch has taken on greater significance, but not enough to be considered a developer favorite. The syntax remained verbose, complex, and very different from the other constructs. The fall-through technique is always complex to manage and error-prone. The scope of construct-level local variables continues to be awkward. For these and other reasons, after more than 20 years, the need was felt to update the construct with new features. In fact, Oracle’s choice to speed up the Java development process through the six-monthly release of new versions, is well suited to the language update process, based on feedback and requests from developers. The platform is rapidly enriching with very interesting new features, after a few years in which its development had suffered an evident slowdown, due in part to the handover, and the consequent need to take new directions between Sun Microsystems and Oracle. The new development cycle of Java-based on the proposals of the developers is giving life to a language that is more and more modern. In particular, with version 12, a new way of introducing a new feature in the language was also inaugurated: the feature preview. In practice, a new feature (the
switch expression) has become usable as a preview in JDK 12. In order to take advantage of the new construct, it was necessary to specify particular options during compilation and at runtime. In this way, the developers had the opportunity to test the switch expressions, and to return fundamental feedback to Oracle. In fact, in version 13 of Java, the implementation of the
switch expressions has been revived with some changes. In particular, the word
yield was introduced, thanks to the feedback from developers. In version 14 therefore, the
switch expressions were confirmed without further changes and were officially declared as new features of the language. So today, you no longer need to specify options to enable feature previews to use
switch expressions, they are effectively a new construct of Java programming.
switch and the Arrow Notation
switch construct, it is now possible to use the arrow notation, which is the symbol consisting of the characters -> that we already use in lambda expressions. It may follow the
case keyword in place of the '
:' symbol. Suppose we have the following enumeration that defines the colors for a semaphore:
and then let’s consider the following class which represents a semaphore:
Now we can rewrite the previous class as follows:
changeColor method, we used a
switch construct with arrow notation, which executes a statement for each
case. Note that there was no need to use the
break keyword. So, the syntax based on arrow notation makes our code less verbose, clearer, and more modern.
Note that to specify multiple statements after arrow notation, you must include them in a pair of curly brackets. For example:
switch and the Fall-through Technique
Consider the following snippet that uses the old syntax of the
switch construct, the
Month enumeration of the
java.time package, and the fall-through technique:
The construct is verbose, not very elegant, and not very readable; furthermore, the syntax of the fall-through technique easily leads to making mistakes. In fact, it is based on the omission of the
break statement that causes the execution of the following
case clauses, simulating what we can do with an
if construct based on multiple boolean expressions bound by
OR operators. For example, we could rewrite everything with an
if construct like the following:
switch and the Fall-through Technique
Now it is possible to use the arrow notation which can follow the keyword
case instead of the symbol '
:'. Let’s rewrite the previous example:
First of all, note that the various
case clauses can declare multiple labels separated by commas. In this way, there is no need to use fall through to perform the same instructions for different
cases, in particular, this syntax prevents us from using fall through. Obviously, there was no need to use the
break keyword. And the syntax is undoubtedly more concise and elegant, even compared to the version that uses the
switch expression evolves the
switch construct that actually suffers from several defects. For example, forgetting a
break means causing an involuntary fall-through. Furthermore, the applicability scenarios are limited compared to a classic
if, and the syntax is rather verbose. For these and other reasons, the
switch is a relatively underutilized construct.
Recall that with the term expression, we mean an instruction (such as a literal value, a method invocation, an operation, etc.) that returns a value. So, a
switch expression is a construct that returns a value. Let’s rewrite the previous example that made use of the arrow notation with a
There are small but important differences compared to the example of the
switch used as a statement seen in the previous section. The main difference is that this
switch expression returns a value that is assigned directly to the
season variable. The returned value is the literal pointed to by the arrow notation. Recall that in the example of the previous section the arrow notation was followed each time by the assignment of the literal to the variable
season. With the
switch expression, on the other hand, we can see an even less verbose syntax that prevents us from rewriting the assignments. Finally, we note that, being an expression, it must end with a semicolon symbol.
The arrow notation can therefore point to an expression that in our example was a literal, but it can also point to a block of code that perhaps contains several instructions. In this
case, to return a value from the code block we can use the word
yield. With it we can specify a value to return. For example, the following
switch expression is equivalent to the one presented in the previous example:
Note, that as expressions we used simple literals (spring and autumn), while in the code blocks we used the new syntax with the
yield statement, which allows us to return a value. In practice, a
yield statement in a
switch expression has the same role as a
return statement in a method.
switchexpression, it is possible to exit only with a value, or by throwing an exception. This means that within a
switchexpression it is not possible to use the
FUN FACT: in version 12 of Java, when
switch expressions were introduced as feature preview, the word
break was used instead of the word
yield. Only in version 13 the word
break was replaced by the word
yield. In fact, the feedback for this feature preview judged the use of the
break to be misleading, as it is already used in other contexts.
FUN FACT 2: Note that we also used the word
var, and that although the compiler usually relies on the left side of the assignment (LHS) to validate the correctness of a
switch expression, when we use the word
var it is based on the right of the assignment (RHS). For more information on the word
var you can read this article.
Arrow vs. Colon
Even with the
switch used as an expression, it is actually still possible to use fall-through. In fact, there is an alternative syntax to the one we saw in the first example, where the arrow notation -> pointed the value to be returned (which can be defined by an expression, or by a block of code). Actually, we can still replace the arrow notation ->, with the notation we used with the ordinary
switch (a.k.a., colon :). We have also seen that the
yield statement in a
switch expression can then specify the value to be returned:
With this syntax,
yield can also be used outside a block of code, and is the only way to return a value. In fact, only with the arrow notation, it is possible to return a literal directly. The most important difference between the two types of syntax, however, is that with the latter it is still possible to use the fall through (even if there is no need for it). In fact, the following syntax is perfectly valid:
Recall that we have already seen in the previous paragraph, that even with the syntax that makes use of the arrow notation
-> it is possible to use the
yield statement, but only within code blocks. For example, this code is valid and equivalent to the others examples seen:
However, it is not possible to mix the two notations (arrow notation and colon notation) in the same construct. For example:
switch expression has among its characteristics that of exhaustiveness. This means that the compiler will not accept situations where a possible
case clause is absent. For example, let’s modify the
TrafficLight class defined previously so that it has a
state variable, which is set using a
switch expression. Voluntarily, however, we do not insert the
we would get the following error when compiling:
which warns us that not all cases have been covered by the construct.
If we had used the
switch as a statement, the code would have been compilable. So, exhaustiveness is a characteristic only of
To have the previous file compiled, it would be enough to re-enable the
RED caseby uncommenting the relative line. Note, however, that the above error will be reported by the compiler only because we are using an enumeration (
Color). In fact, when compiling the TrafficLight.java file, the compiler can check the enumeration to evaluate what all its elements are. However, this would not have been possible if instead of an enumeration we had had a string, an integer or a wrapper type. In fact, in these cases, we do not have a finite number of values to be assigned, and therefore the only way to cover all cases is to add a
defaultclause. Therefore, in a
defaultclause should always be used, except in the
caseof using an enumeration as a value to be tested.
default Clause and Exhaustiveness
Referring to the previous example, obviously, nobody forbids us to use a
default clause in place of the missing
RED case, but this solution, only in the case of enumerations, could be harmful! It could also create problems if we want to add both clauses (
RED case and
default) as below:
In fact, suppose that the
Color enumeration evolves to define the
BLACK color, which will be used to manage situations in which the traffic light is off:
When we compile, the
default clause would prevent the compiler from warning us that we are not covering all possible cases and we will only discover the problem at runtime. So, by launching the following test class:
We will obtain the following output:
With Java 14 we have a renewed
switch construct that is much more interesting than the one we have used for many years. In particular, the arrow notation, and the possibility of grouping the values of several cases, separating them with commas, guarantees us less verbosity and greater robustness of our code. We can still use the classic colon notation (and consequently the fall-through technique) for compatibility reasons with code written with previous versions of Java, however, the old syntax will probably be abandoned in the future. The word
yield allows you to return a value in
switch expressions, just as the word
return allows you to return a value in methods. Finally, the concept of exhaustiveness of
switch expressions grants us a compile-level check of the completeness of the definition when we use enumerations. The new
switch therefore represents another fundamental reason for going beyond Java 8.
Published at DZone with permission of Claudio De Sio Cesari. See the original article here.
Opinions expressed by DZone contributors are their own.