Learnitweb

Java switch expressions

1. Introduction

There were two problems of switch statements: default control flow behavior of switch blocks and the default scoping of switch blocks. To solves these issues, switch expression was introduced. Switch expression was a preview language feature in JDK 12 and JDK 13. With switch expressions switch was extended to be used either a statement or an expression. The new switch expression can use both traditional : labels or new -> labels. The switch expression added a new statement for yielding a value from a switch expression.

Following is an example of switch expression:

switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
    case TUESDAY                -> System.out.println(7);
    case THURSDAY, SATURDAY     -> System.out.println(8);
    case WEDNESDAY              -> System.out.println(9);
}

The code following a case L -> label in a switch expression can be an expression, a block of code, or a throw statement.

Here is an example of traditional switch:

switch (day) {
    case MONDAY:
    case TUESDAY:
        int temp = ...     // The scope of 'temp' is till }
        break;
    case WEDNESDAY:
    case THURSDAY:
        int temp2 = ...    // Can't name the variable 'temp'
        break;
    default:
        int temp3 = ...    // Can't name the variable 'temp'
}

Here the variable temp has the entire scope of switch till }.

2. Using switch expression

A new case L -> label was introduced. When a label matches, only the expression or statement to the right of the arrow is executed, with no fall-through to subsequent cases.

The new kind of case label has the following form:

case label_1, label_2, ..., label_n -> expression;|throw-statement;|block 

When the Java runtime finds a matching label to the left of the arrow, it executes the corresponding code to the right of the arrow without falling through to other cases. If the code to the right is an expression, its value becomes the result of the switch expression.

Here is an example of switch expression:

public class SwitchExpressionExample {
    public static void main(String[] args) {
        int month = 5;

        // Switch expression to get the name of the month
        String monthName = switch (month) {
            case 1 -> "January";
            case 2 -> "February";
            case 3 -> "March";
            case 4 -> "April";
            case 5 -> "May";
            case 6 -> "June";
            case 7 -> "July";
            case 8 -> "August";
            case 9 -> "September";
            case 10 -> "October";
            case 11 -> "November";
            case 12 -> "December";
            default -> throw new IllegalArgumentException("Invalid month: " + month);
        };

        // Switch expression to determine the season
        String season = switch (month) {
            case 12, 1, 2 -> "Winter";
            case 3, 4 -> "Spring";
            case 5, 6 -> "Summer";
            case 7, 8 -> "Rain";
            case 9, 10, 11 -> "Fall";
            default -> throw new IllegalArgumentException("Invalid month: " + month);
        };

        System.out.println("The month is: " + monthName);
        System.out.println("The season is: " + season);
    }
}

The switch statement can be used as an expression. Here is an example:

T result = switch (arg) {
    case L1 -> e1;
    case L2 -> e2;
    default -> e3;
};

3. Exhaustiveness of switch Statements

The cases of a switch expression must be exhaustive, which means that for all possible values, there must be a matching switch label.

The following example doesn’t compile because the switch statement (which uses pattern labels) is not exhaustive and does not cover all possible input values.

static void test(Object obj) {
    switch (obj) {  
        case String s: 
            System.out.println(s);
            break;
        case Integer i:
            System.out.println("Integer");
            break;
    }    
}

You can make it exhaustive by adding a default clause:

static void test(Object obj) {
        switch (obj) { 
            case String s: 
                System.out.println(s);
                break;
            case Integer i:
                System.out.println("Integer");
                break;
            default:
                break;
        }    
    }

In addition, a switch expression must either return a value or terminate abruptly by throwing an exception. This requirement leads to several implications. Firstly, the compiler ensures that every switch label yields a value if it matches.

int i = switch (day) {
    case MONDAY -> {
        System.out.println("Monday"); 
        // ERROR! Block doesn't contain a yield statement
    }
    default -> 1;
};
i = switch (day) {
    case MONDAY, TUESDAY, WEDNESDAY: 
        yield 0;
    default: 
        System.out.println("Second half of the week");
        // ERROR! Group doesn't contain a yield statement
};

A switch expression must evaluate to a single value (or throw an exception), you can’t jump through a switch expression with a break, yield, return, or continue statement, like in the following example:

someLabel: 
    for (int i = 0; i < 99; ++i) {
        int k = switch (e) { 
            case 0:  
                yield 1;
            case 1:
                yield 2;
            default: 
                continue z; 
                // ERROR! can't jump through a switch expression with continue 
        };
    // ...
    }

4. Yielding a value

Typically, switch expressions use a single expression after the case L -> label. If a more complex block of code is required, a yield statement can be used within the block to provide a value for the switch expression.

public class SwitchExpressionYieldExample {
    public static void main(String[] args) {
        int dayOfWeek = 6;

        // Switch expression to determine if the day is a weekday or weekend
        String dayType = switch (dayOfWeek) {
            case 1, 2, 3, 4, 5 -> "Weekday";
            case 6, 7 -> {
                System.out.println("It's the weekend!");
                yield "Weekend";
            }
            default -> throw new IllegalArgumentException("Invalid day of the week: " + dayOfWeek);
        };

        System.out.println("Day type: " + dayType);
    }
}

A switch expression, similar to a switch statement, can use traditional switch blocks with case L: labels, which imply fall-through semantics. In such cases, values are provided using the yield statement.

int result = switch (s) {
    case "Foo": 
        yield 1;
    case "Bar":
        yield 2;
    default:
        System.out.println("Neither Foo nor Bar");
        yield 0;
};

5. Conclusion

Switch expressions were introduced as a preview feature in Java 12 and became a standard feature in Java 14. They provide a more concise and expressive way to handle multiple conditional branches compared to traditional switch statements.