Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

JDK 12: Switch Statements/Expressions in Action

DZone's Guide to

JDK 12: Switch Statements/Expressions in Action

Want to learn more about the use of Switch statements and expressions in JDK 12 Early Access Builds? Check out this post to learn more!

· Java Zone ·
Free Resource

How do you break a Monolith into Microservices at Scale? This ebook shows strategies and techniques for building scalable and resilient microservices.

My last post "Playing with JDK 12's Switch Expressions" talked about the use of the JDK 12 Early Access Builds to try out JEP 325 switch expressions and statements and provided a simple example. This post uses the same JDK 12 Early Access Build 10 to demonstrate different features of switch expressions and enhanced switchstatements.

I used a 2x2 grid in the blog post "Enhancing Java switch Statement with Introduction of switch Expression" to illustrate that the new "arrow" syntax ("switch labeled rule") can be used with a switchstatement or with a switch expression per JEP 325. Similarly, with JEP 325, the traditional "colon" syntax ("switch labeled statement group") can also be used with either a switch expression or with a switch statement. In other words, the presence of the colon (:) does NOT necessarily imply a switch statement, and the presence of an "arrow" (->) does NOT necessarily imply a switchexpression. For convenience, I've included an adapted version of the table shown in my earlier post here.

STATEMENT
("Nonlocal control flow _out_ of a switch [continue to an enclosing loop, break with label, return]")
EXPRESSION
(Totality: return a value)
SWITCH LABELED STATEMENT GROUP
("Colon")
(Enables Fall-through)
switch we know and "love", but enhanced break returns a value like return
SWITCH LABELED RULE
("Arrow")
(Prevents Fall-through)
"Syntactic shorthand" for Statement/Colon (above) plus
  • "obviates the annoyance of 'break'"
  • "implicitly prevents fallthrough of all forms"
  • "avoids the confusion of current switch scoping"
Arrow (->) points to returned value

With the JDK 12 Early Access Builds, it is convenient to try the new switch expression out, and we can also try out the traditional and enhanced versions of the switchstatement.

Traditional switch Statement

The traditional switchstatement that we "know and love" is still available, even with the JDK 12 preview enabled (--enable-preview). An example of this traditionalswitch statement that compiles and executes successfully with the JDK 12 language feature preview enabled is shown below.

/** 
 * Demonstrate traditional switch statement assigned to 
 * local variable. 
 */  
public static void demonstrateTraditionalSwitchStatement()  
{  
   out.println("Traditional Switch Statement:");  
   final int integer = 3;  
   String numericString;  
   switch (integer)  
   {  
      case 1 :  
         numericString = "one";  
         break;  
      case 2 :  
         numericString = "two";  
         break;  
      case 3:  
         numericString = "three";  
         break;  
      default:  
         numericString = "N/A";  
   }  
   out.println("\t" + integer + " ==> " + numericString);  
}  


This and all other code examples that are demonstrated in this post are available on GitHub. This particular example shows a common use of the traditional switch statement to set a local variable's value. I intentionally chose this use case because a new switch expression is an improved approach to accomplishing this.

Enhanced switch Statement

As stated previously, we can use the new "arrow" syntax ("switch labeled rules") with the enhancedswitch statement. This is shown in the next code example that compiles and runs against the JDK 12 Early Access Build 10 when --enalved-preview is used.

/** 
 * Demonstrate enhanced switch statement used to assign 
 * a local variable. 
 */  
public static void demonstrateEnhancedSwitchStatement()  
{  
   out.println("Enhanced Switch Statement:");  
   final int integer = 2;  
   String numericString;  
   switch (integer)  
   {  
      case 1 -> numericString = "one";  
      case 2 -> numericString = "two";  
      case 3 -> numericString = "three";  
      default -> numericString = "N/A";  
   }  
   out.println("\t" + integer + " ==> " + numericString);  
}  


This last example shows the switch still being used as a statement, but in this case, it takes advantage of the "arrow" syntax ("label rules") to accomplish its switching without explicit specification of break. This is not only less code, but more importantly, it has the advantage of not allowing for the often dreaded switch"fall-through." In short, the enhancedswitch statement works like the current/traditional switchstatement, but without the potential warts of the traditional version.

New switch Expression Returning Value via break

Beyond enhancing the current switch statement to allow for the specification of a switch statement without risk of fall-through, JEP 325 also introduces the concept of using the switch keyword in a switch expression. The Java Tutorial's "Expressions, Statements, and Blocks" page explains the differences between statements and operations. For the purposes of this discussion, two of the important observations made in that tutorial include:

  1. "An expression is a construct made up of variables, operators, and method invocations ... that evaluates to a single value."
  2. "The Java programming language allows you to construct compound expressions from various smaller expressions as long as the data type required by one part of the expression matches the data type of the other."

The next code listing demonstrates how, with JDK 12 Early Access Build 10 and --enable-preview, one can replace the code shown above that used a switch statement to assign a value to an earlier declared local variable with a single statement that uses a switch expression to assign its result value to the local variable in a single statement.

/** 
 * Demonstrate switch expression using colons and breaks. 
 */  
public static void demonstrateSwitchExpressionWithBreaks()  
{  
   final int integer = 1;  
   out.println("Switch Expression with Colons/Breaks:");  
   final String numericString =  
      switch (integer)  
      {  
         case 1 :  
            break "uno";  
         case 2 :  
            break "dos";  
         case 3 :  
            break "tres";  
         default :  
            break "N/A";  
      };  
   out.println("\t" + integer + " ==> " + numericString);  
} 


The code example just shown demonstrates the use of a switchexpression that looks very similar to the traditional switch statement example shown earlier. However, there are a couple of significant differences. One difference is that this switch expression returns a result that is assigned to the local variable "numericString." The second difference, which directly relates to the switch expression being able to return a value, is that the break clauses now each have the value to be returned for the relevant case immediately specified after the break keyword. In essence, the break in the switch expression acts like athe Java method return.

New switch Expression Returning Value via Label Rules

The example just shown demonstrates that one can return a value from a switch expression with a similar colon (:) and break syntax to what one is likely used with switch statements. Besides being familiar, the other advantage of this is that one can specify multiple statements to occur for a single case before returning a single value. In most cases, however, it will likely become popular to return a value from a switch expression using the "arrow" syntax discussed earlier to benefit from no risk of fall-through and to avoid scope surprises commonly associated with the traditional switch statement. The next code listing demonstrates how the new switch expression can use "label rules" ("arrow" syntax) instead of a colon and break to elegantly return a single resolved value for the switch.

/** 
 * Demonstrate switch expressions using "arrow" syntax. 
 */  
public static void demonstrateSwitchExpressionWithArrows()  
{  
   final int integer = 4;  
   out.println("Switch Expression with Arrows:");  
   final String numericString =  
      switch (integer)  
      {  
         case 1 -> "uno";  
         case 2 -> "dos";  
         case 3 -> "tres";  
         case 4 -> "quatro";  
         default -> "N/A";  
      };  
   out.println("\t" + integer + " ==> " + numericString);  
}


The four examples above each demonstrate the cases shown in the 2x2 grid. The remainder of this post will discuss some additional observations from trying out switch expressions and statements with the JDK 12 Early Access Build 10.

Multiple Constants Can Be Specified for a Single case

Any of the four quadrants in the 2x2 grid allow for multiple constants to be associated with a single case. This is demonstrated in the next code listing that compiles and runs with JDK 12 Early Access Build 10 with "preview language features" enabled.

/** 
 * Demonstrate that multiple constants can be associated with 
 * a single {@code case} and used in conjunction with a 
 * {@code switch} expression that uses the "arrow" syntax. 
 */  
public static void demonstrateLabelRulesWithSharedCases()  
{  
   final int integer = 7;  
   out.println("Multiple Case Labels:");  
   final String numericString =  
      switch (integer)  
      {  
         case 0 -> "zero";  
         case 1, 3, 5, 7, 9 -> "odd";  
         case 2, 4, 6, 8, 10 -> "even";  
         default -> "N/A";  
      };  
   out.println("\t" + integer + " ==> " + numericString);  
}  
  
/** 
 * Demonstrate that multiple constants can be associated with 
 * a single {@code case} and used in conjunction with a 
 * {@code switch} statement that uses the traditional colon and 
 * {@code break} syntax. 
 */  
public static void demonstrateBlockedStatementsWithSharedCases()  
{  
   final int integer = 6;  
   out.println("Multiple Case Labels:");  
   String numericString;  
   switch (integer)  
   {  
      case 0:  
         numericString = "zero";  
         break;  
      case 1, 3, 5, 7, 9:  
         numericString = "odd";  
         break;  
      case 2, 4, 6, 8, 10:  
         numericString = "even";  
         break;  
      default:  
         numericString = "N/A";  
   };  
   out.println("\t" + integer + " ==> " + numericString);  
}  


Arrow and Colon/break ("Statement Group") Cannot Be Mixed

The JDK 12 Early Access Build 10 compiler (javac) does NOT allow the mixing of the "arrow" syntax and the traditional colon/break syntax. Attempting to mix these results in the error message: "error: different kinds used in the switch." An example of code that would not compile and would show this particular error message is shown next.

/** 
 * WARNING - This does NOT compile, even with JDK 12 Early 
 * Access Builds and --enable-preview because JEP 325 does 
 * not allow the "arrow" syntax to be mixed with the 
 * traditional colon/break syntax. 
 */  
public static void demonstrateMixed()  
{  
   final int integer = 3;  
   String numericString;  
   switch(integer)  
   {  
      case 1 :  
         numericString = "one";  
         break;  
      case 2 -> numericString = "two";  
      default -> numericString = "N/A";  
   }  
   return numericString;  
}  


switch Statement's break Cannot Return Value

The new switch expression returns a value, and when the colon and break approach are used by the switch expression, that returned value is designated immediately following the break keyword. Because the traditional switch statement does not return a value, it is a compile-time error to attempt to have a break associated with a switchstatement designate a return value. The error ("error: unexpected value break") can be reproduced with the following code.

/** 
 * WARNING - This does NOT compile, even with JDK 12 Early 
 * Access Builds and --enable-preview because it is 
 * nonsensical to have a "statement" return a value; that 
 * is what an expression should be used for. 
 */  
public static void demonstrateSwitchStatementReturnedLabel()  
{  
   final int integer = 4;  
   switch (integer)  
   {  
      case 1:  
         break "one";  
      case 2:  
         break "two";  
      case 3:  
         break "three";  
      default:  
         break "N/A";  
   };  
} 


When one attempts to compile the above code using JDK 12 Early Access Build 10's javac compiler with flags --enable-preview and -release 12 specified, four instances (corresponding to the three case plus one default) of the error message "error: unexpected value break" are seen. Not surprisingly, the simple change of assigning this switch to a local variable (and effectively turning the statement into an expression) allows this code to compile. In other words, changing the code above to the code in the next code listing allows it to compile and run successfully.


/** 
 * This demonstrates that a {@code switch} "expression" is 
 * able to (and expected to) provide the "return" value for 
 * a given {@code case} and {@code default} instead of being 
 * a compiler error as it was for the "statement" example 
 * demonstrated in method 
 * {@link #demonstrateSwitchStatementReturnedLabel()}. 
 */  
public static void demonstrateSwitchExpressReturnedLabel()  
{  
   final int integer = 4;  
   final String numericString =  
   switch (integer)  
   {  
      case 1:  
         break "one";  
      case 2:  
         break "two";  
      case 3:  
         break "three";  
      default:  
         break "N/A";  
   };  
}  


The current JEP 325 text includes a discussion of how this break behavior is similar to methods' return. That discussion points out that the switch statement requiring no returned value after its breaks is analogous to a method returning void. A switchexpression is expected to return a non-void value.

switch Statement's "Arrow" Syntax Must Point to a Statement

The following code will not compile with JDK 12 Early Access Build 10, even with --enable-preview and -release 12 provided to the javac compiler.

/** 
 * WARNING - This does not compile, even with JDK 12 Early 
 * Access Builds and --enable-preview and reports error message 
 * "error: not a statement" because it is expecting a 
 * {@code switch} "statement" but what is being provided to each 
 * {@code case} is NOT a statement. 
 */  
public static void demonstrateSwitchStatementReturnedValueViaLabelRule()  
{  
   final int integer = 5;  
   switch (integer)  
   {  
      case 1 -> "one";  
      case 2 -> "two";  
   };  
   out.println(numericString);  
}  


The above code does not compile and the error message reported is, "error: not a statement." This is because the switch is being used as a statement in this example, but the "arrow" syntax is "pointing" to literal strings rather than to a valid Java statement.

All Possibilities Must Be Specified in a switch Expression

Because a switch expression needs to return a non-void value, a switch expression must specify a casefor all possible values it might switch on. In practice, this is likely to be accomplished via a default to catch all possibilities not explicitly specified with case. With a traditional switch statement, it was not required to ensure that all possible values being switched on were covered by a case or default and that led sometimes to conditions such as I described in the blog post "Log Unexpected Switch Options."

The following code violates the rule that a switch expression must specify all possible values in either a case or via default:


/** 
 * WARNING - This method will not compile even with JDK 12 
 * Early Access Build 10 with --enable-preview because of 
 * error; "the switch expression does not cover all possible 
 * input values". 
 */  
public static void demonstrateLackingCaseInSwitchExpression()  
{  
   final int integer = 5;  
   String numericString =  
      switch (integer)  
      {  
         case 1 -> "one";  
         case 2 -> "two";  
      };  
   out.println(numericString);  
} 


The code just shown will not compile and the causal error message is, "error: the switch expression does not cover all possible input values."

The Effect of JEP 325 on Future Use of switch

Considering the possibilities presented by the availability of switch expressions in Java, in addition to switchstatements, and considering the advantages offered by the new "arrow" syntax that can be used with switch expressions or statements, it is interesting to begin thinking about when each quadrant in the above 2x2 grid is most beneficial. In general, I believe I will find myself using the switch expression with "arrow" syntax ("label rules") most often with enhanced switch statements using "arrow" syntax also being frequently used. I suspect I'll use the traditional : (break) syntax far less often in the future. Even when I have multiple statements to be executed for a particular case, I'll likely factor those statements into a single method that can be called in the case using the "arrow" syntax. This will allow me to benefit from more obvious scoping and avoid the risks of fall-through. Given the ability to specify multiple constants for a single case that will now be available, fall-through won't be necessary anymore, even in cases where multiple cases lead to the same result.

Additional Resources

How do you break a Monolith into Microservices at Scale? This ebook shows strategies and techniques for building scalable and resilient microservices.

Topics:
java ,jdk ,jdk 12 ,switch statements ,switch expressions ,tutorial ,eab ,early access builds

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}