Switch expressions in Java 12

With switch expressions, Java 12 is enhancing one of its basic language  constructs – switch – to improve everyday coding experience for developers. Benefits are multi-fold – from reduced code verbosity, better code readability to no implicit fall-through ambiguity through its case labels.

Code semantics for Switch expressions

Here’s an example of new switch expression, which modifies a variable value, based on an enum value passed to a method:

enum SingleUsePlastic {STRAW, BAG, SPOON, FORK, KNIFE, PLATE, BOTTLE};
class Planet {
    private static long damage;
    public void use(SingleUsePlastic plastic) {
        damage += switch(plastic) {
                case STRAW -> 10;
                case BAG -> 11;
                case SPOON, FORK, KNIFE -> 7;
                case PLATE -> 15;
                case BOTTLE -> 20;
       };
    }
}

The new switch expression shown in the preceding code is a pleasure to read and comprehend. It lets the business logic take the centre stage.

The following image highlights the changes:

The new switch expressions are in addition to the traditional switch constructs. They are not replacing the existing switch constructs.

A switch expression offers multiple benefits and features:

  • Unlike a switch construct, a switch expression can return a value
  • The return value of a switch case is followed by ->
  • The case labels can include comma separated multiple values
  • There isn’t any fall through of the control across switch case labels
  • The code is easy to read and understand

Issues with the traditional switch constructs

The current syntax of switch constructs is constrained and verbose. It often leads to error prone code, which is difficult to debug.

Let’s rewrite the preceding example using the traditional switch statement:

enum SingleUsePlastic {STRAW, BAG, SPOON, FORK, KNIFE, PLATE, BOTTLE};
class Planet {
    private static long damage;
    public void use(SingleUsePlastic plastic) {
        switch (plastic) {
            case STRAW  : damage += 10;
                          break;
            case BAG    : damage += 11;
                          break;
            case SPOON  : damage += 7;
                          break;
            case FORK   : damage += 7;
                          break;
            case KNIFE  : damage += 7;
                          break;
            case PLATE  : damage += 15;
                          break;
            case BOTTLE : damage += 20;
                          break;
       };
    }
}

The arms (case labels) in switch constructs must include a break statement to prevent fall-through of the control. This essentially means that when control finds a matching case value, it will execute the statements until it finds a break statement, or it reaches the end of the switch construct.

Let’s modify the preceding code, replacing the break statement corresponding to case label KNIFE with System.out.println() statement:

case KNIFE  : damage += 7;
              System.out.println(7);

With this modified code, when you call use(SingleUsePlastic.KNIFE), the following happens:

  • Control falls-through the arm corresponding to value PLATE
  • Variable damage increments by 22 (7 for KNIFE and 15 for PLATE).

The traditional switch construct have a default fall-through of control across the case labels, in absence of a break statement. This leads to unexpected bugs.

The new switch expressions is here to bring the spotlight back to the business logic.

Code blocks, local variables and extended break statement

The switch expressions can define a block of code to execute for a matching value. The block is defined using a pair of curly braces and it must include a break statement specifying the value to return.

Let’s modify code from our previous example and define a code block corresponding to case PLATE:

class Planet {
    private static long damage;
    public void use(SingleUsePlastic plastic) {
        damage += switch(plastic) {
            case STRAW -> 10;
            case BAG -> 11;
            case SPOON, FORK, KNIFE -> 7;
            case PLATE -> {
                                int radius = ..;
                                break (radius < 10 ? 15 : 20); // Using break to 
                                                               // return a value
                          }
            case BOTTLE -> 20;
        };
    }
}

The case label for value PLATE defines a block statement. A block can also define local variables (radius in this case). The scope and accessibility of local variable radius is limited to the case label PLATE.

Notice how a switch expression uses a break statement to return a value (all case labels of a switch expression must return a value).

Another syntax for switch expressions

Apart from using -> to specify a return value, a switch expression can also use a colon (:) to mark the beginning of code to execute and a break statement to return a value. Here’s an example:

class Planet {
    private static long damage;
    public void use(SingleUsePlastic plastic) {
        damage += switch(plastic) {
            case STRAW : break 10;      // Use : to start code, break to return val
            case BAG : break 11;
            case SPOON, FORK, KNIFE : break 7;
            case PLATE :      // no need for using curly brace
                                int radius = ..;
                                break (radius < 10 ? 15 : 20); // Using break to 
                                                               // return a value
            case BOTTLE : break 20;
        };
    }
}

Comparing break with break <return value>

A traditional switch construct uses a break statement (without any return value) to break out of a switch construct when a matching value is found. It also prevents fall-through of the control across multiple case labels. A break statement with a return value is used in switch expressions to return a value and return the control from a switch expression.

You can compare these flavors of break with a return statement with similar flavors. In a method, you can use a return statement to return a value and exit a method, or just exit a method without returning a value. Here’s a quick example:

int calculate(int value1, int value2) {   // return type of method is int
    int result = …
    return result;                        // returns int value
}

void output(int value1, int value2) {     // return type of method is void
    int result = …
    if (result > 10)
        return;                           // Just exit method without returning
    else                                  // any value.
        System.out.println(…);
}

A switch expression uses break to return a value. Traditional switch construct uses break to prevent fall-through of control across its case labels.

Returning values for all case labels

All case labels must return a value in a switch expression. If you define a code block in a switch construct and miss returning a value, the code won’t compile:

class Planet {
    private static long damage;
    public void use(SingleUsePlastic plastic) {
        damage += switch(plastic) {
            case SPOON, FORK, KNIFE -> 7;
            case PLATE -> {
                                int radius = ..;
                              // no break statement
                          }
        };
    }
}

In the preceding code, since the code block for label PLATE doesn’t return a value using the break statement, it won’t compile.

Similarly, if you use a colon (:) to define the case labels of a switch expression, all its labels must include a break statement with a return value. Otherwise your code won’t compile:

class Planet {
    private static long damage;
    public void use(SingleUsePlastic plastic) {
        damage += switch(plastic) {
            case SPOON, FORK, KNIFE : break 7;   // this is okay
            case PLATE : int radius = ..;
                         // no break statement   // Won’t compile
        };
    }
}

All case labels in a switch expression must return a value, or else the switch expression won’t compile.

Executing code with switch expressions

As of now, switch expression is a preview feature, that is, it might be removed in a future Java version. To compile source code, say, Planet.java, with switch expressions, you’ll need JDK 12 (downloadable from https://jdk.java.net/12/).

Compile your code with the following command line options:

> javac --release 12 --enable-preview Planet.java

You can execute it as follows:

> java --enable-preview Planet

 

- Mala Gupta

Leave a Reply

Your email address will not be published. Required fields are marked *