11 Switch Expressions and Statements
You can use the switch keyword as either a statement or an
        expression. Like all expressions, switch expressions evaluate to a single
        value and can be used in statements. Switch expressions may contain "case L
            ->" labels that eliminate the need for break statements to
        prevent fall through. You can use a yield statement to specify the value of
        a switch expression.
               
 For background information about the design of switch expressions, see
                JEP 361.
               
Arrow Cases
Consider the following switch statement that prints the
        number of letters of a day of the week:
                  
public enum Day { SUNDAY, MONDAY, TUESDAY,
    WEDNESDAY, THURSDAY, FRIDAY, SATURDAY; }
// ...
int numLetters = 0;
Day day = Day.WEDNESDAY;
switch (day) {
    case MONDAY:
    case FRIDAY:
    case SUNDAY:
       numLetters = 6;
        break;
    case TUESDAY:
        numLetters = 7;
        break;
    case THURSDAY:
    case SATURDAY:
        numLetters = 8;
        break;
    case WEDNESDAY:
        numLetters = 9;
        break;
}
System.out.println(numLetters);It would be better if you could "return" the length of the day's name instead
            of storing it in the variable numLetters; you can do this with a
                switch expression. Furthermore, it would be better if you didn't
            need break statements to prevent fall through; they are laborious to
            write and easy to forget. You can do this with an arrow case. The following is a
                switch expression that uses arrow cases to print the number of
            letters of a day of the week:
                  
Day day = Day.WEDNESDAY;    
System.out.println(
    switch (day) {
        case MONDAY, FRIDAY, SUNDAY -> 6;
        case TUESDAY                -> 7;
        case THURSDAY, SATURDAY     -> 8;
        case WEDNESDAY              -> 9;
    }
);    An arrow case has the following form:
case label_1, label_2, ..., label_n -> expression;|throw-statement;|block When the Java runtime matches any of the labels to the left of the arrow, it
            runs the code to the right of the arrow and does not fall through; it does not run any
            other code in the switch expression (or statement). If the code to the
            right of the arrow is an expression, then the value of that expression is the value of
            the switch expression.
                  
You can use arrow cases in switch statements. The following
            is like the first example, except it uses arrow cases instead of colon cases:
                  
int numLetters = 0;
Day day = Day.WEDNESDAY;
switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> numLetters = 6;
    case TUESDAY                -> numLetters = 7;
    case THURSDAY, SATURDAY     -> numLetters = 8;
    case WEDNESDAY              -> numLetters = 9;
};
System.out.println(numLetters);An arrow case along with its code to its right is called a
                switch-labeled rule.
                  
Colon Cases and the the yield Statement
A colon case is a case label in the form
            case L:. You can use colon cases in switch
        expressions. A colon case along with its code to the right is called a
            switch-labeled statement group:
                  
Day day = Day.WEDNESDAY;
int numLetters = switch (day) {
    case MONDAY:
    case FRIDAY:
    case SUNDAY:
        System.out.println(6);
        yield 6;
    case TUESDAY:
        System.out.println(7);
        yield 7;
    case THURSDAY:
    case SATURDAY:
        System.out.println(8);
        yield 8;
    case WEDNESDAY:
        System.out.println(9);
        yield 9;
};
System.out.println(numLetters);The previous example uses yield statements. They take one
            argument, which is the value that the colon case produces in a switch
            expression.
                  
The yield statement makes it easier for you to differentiate
            between switch statements and switch expressions. A
                switch statement, but not a switch expression, can
            be the target of a break statement. Conversely, a
                switch expression, but not a switch statement, can
            be the target of a yield statement.
                  
Note:
It's recommended that you use arrow cases. It's easy to forget to insert
                    break or yield statements when using colon
                cases; if you do, you might introduce unintentional fall through in your code.
                     
For arrow cases, to specify multiple statements or code that are not
                expressions or throw statements, enclose them in a block. Specify
                the value that the arrow case produces with the yield
                statement:
                     
int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> {
        System.out.println(6);
        yield 6;
    }
    case TUESDAY -> {
        System.out.println(7);
        yield 7;
    }
    case THURSDAY, SATURDAY -> {
        System.out.println(8);
        yield 8;
    }
    case WEDNESDAY -> {
        System.out.println(9);
        yield 9;
    }
};  Qualified enum Constants as case Constants
You can use qualified enum constants as
            case constants in switch expressions and
        statements.
                  
Consider the following switch expression whose selector expression is an
                enum type:
                  
public enum Standard { SPADE, HEART, DIAMOND, CLUB }
// ...
static void determineSuitStandardDeck(Standard d) {
    switch (d) {
        case SPADE   -> System.out.println("Spades");
        case HEART   -> System.out.println("Hearts");
        case DIAMOND -> System.out.println("Diamonds");
        default      -> System.out.println("Clubs");   
    }
}In the following example, the type of the selector expression is an
            interface that's been implemented by two enum types. Because the type
            of the selector expression isn't an enum type, this
                switch expression uses guarded patterns instead:
                  
sealed interface CardClassification permits Standard, Tarot {}
public enum Standard implements CardClassification
    { SPADE, HEART, DIAMOND, CLUB }
public enum Tarot implements CardClassification
    { SPADE, HEART, DIAMOND, CLUB, TRUMP, EXCUSE }
// ...
static void determineSuit(CardClassification c) {
    switch (c) {
        case Standard s when s == Standard.SPADE    -> System.out.println("Spades");
        case Standard s when s == Standard.HEART    -> System.out.println("Hearts");
        case Standard s when s == Standard.DIAMOND  -> System.out.println("Diamonds");
        case Standard s                             -> System.out.println("Clubs");   
        case Tarot t when t == Tarot.SPADE          -> System.out.println("Spades or Piques");
        case Tarot t when t == Tarot.HEART          -> System.out.println("Hearts or C\u0153ur");
        case Tarot t when t == Tarot.DIAMOND        -> System.out.println("Diamonds or Carreaux");
        case Tarot t when t == Tarot.CLUB           -> System.out.println("Clubs or Trefles");
        case Tarot t when t == Tarot.TRUMP          -> System.out.println("Trumps or Atouts");
        case Tarot t                                -> System.out.println("The Fool or L'Excuse");
    }
}However, switch expressions and statements allow qualified
                enum constants, so you could rewrite this example as follows:
                  
static void determineSuitQualifiedNames(CardClassification c) {
    switch (c) {
        case Standard.SPADE   -> System.out.println("Spades");
        case Standard.HEART   -> System.out.println("Hearts");
        case Standard.DIAMOND -> System.out.println("Diamonds");
        case Standard.CLUB    -> System.out.println("Clubs");   
        case Tarot.SPADE      -> System.out.println("Spades or Piques");
        case Tarot.HEART      -> System.out.println("Hearts or C\u0153ur");
        case Tarot.DIAMOND    -> System.out.println("Diamonds or Carreaux");
        case Tarot.CLUB       -> System.out.println("Clubs or Trefles");
        case Tarot.TRUMP      -> System.out.println("Trumps or Atouts");
        case Tarot.EXCUSE     -> System.out.println("The Fool or L'Excuse");
    }
}Therefore, you can use an enum constant when the type of the selector
            expression is not an enum type provided that the enum
            constant's name is qualified and its value is assignment-compatible with the type of the
            selector expression.
                  
Primitive Values in switch Expressions and Statements
In addition to the primitive types char,
            byte, short, and int, a
            switch expression or statement's selector expression can be of type
            long, float, double, and
            boolean, as well as the corresponding boxed types.
                  
Note:
This is a preview feature. A preview feature is a feature whose design, specification, and implementation are complete, but is not permanent. A preview feature may exist in a different form or not at all in future Java SE releases. To compile and run code that contains preview features, you must specify additional command-line options. See Preview Language and VM Features.See JEP 455: Primitive Types in Patterns, instanceof, and switch (Preview) for additional information.
If a selector expression is of type long,
                float, double, and boolean, then
            its case labels must have the same type as the selector expression or its corresponding
            boxed type. For example:
                  
void whichFloat(float v) {
    switch (v) {
        case 0f ->
            System.out.println("Zero");
        case float x when x > 0f && x <= 10f ->
            System.out.println(x + " is between 1 and 10");
        case float x ->
            System.out.println(x + " is larger than 10");
    }            
}If you change the case label 0f to 0, you would get the
            following compile-time error:
                  
error: constant label of type int is not compatible with switch selector type floatYou can't use two floating-point literals as case labels
            that are representationally equivalent. (See the JavaDoc API documentation for
            the Double class for more information about representation
            equivalence.) The following example generates a compiler error. The value
                0.999999999f is representationally equivalent to
                1.0f:
                  
void duplicateLabels(float v) {
    switch (v) {
        case 1.0f -> System.out.println("One");
        // error: duplicate case label
        case 0.999999999f -> System.out.println("Almost one");
        default -> System.out.println("Another float value");
    }            
}Switching on boolean values is a useful alternative to the ternary
            conditional operator (?:) because a boolean
                     switch expression or statement can contain statements as well as
            expressions. For example, the following code uses a boolean
                     switch expression to perform some logging when
            false:
                  
long startProcessing(OrderStatus.NEW, switch (user.isLoggedIn()) {
    case true  -> user.id();
    case false -> { log("Unrecognized user"); yield -1L; }
});Exhaustiveness of switch
The cases of a switch expression or statement must be
            exhaustive, which means that for all possible values, there must be a matching
            case label. Thus, a switch expression or statement
        normally require a default clause. However, for an enum
                     switch expression that covers all known constants, the compiler inserts an
        implicit default clause, like the examples at the beginning of this section
        that print the number of letters in name of a day of the week.
                  
The cases of a switch statement must be exhaustive if it uses pattern or
                null labels. See Pattern Matching with switch and Null case Labels for more information.
                  
The following switch expression is not exhaustive and
            generates a compile-time error. The type coverage of its labels consist of the subtypes
            of String and Integer. However, it doesn't include the
            type of the selector expression, Object:
                  
static int coverage(Object obj) {
    return switch (obj) {         // Error - not exhaustive
        case String s  -> s.length();
        case Integer i -> i;
    };
}However, the type coverage of the case label default is all
            types, so the following example compiles:
                  
static int coverage(Object obj) {
    return switch (obj) { 
        case String s  -> s.length();
        case Integer i -> i;
        default        -> 0;
    };
}Consequently, for a switch expression or statement to be
            exhaustive, the type coverage of its labels must include the type of the selector
            expression.
                  
The compiler takes into account whether the type of a selector expression is
            a sealed class. The following switch expression compiles. It doesn't
            need a default case label because its type coverage is the classes
                A, B, and C, which are the only
            permitted subclasses of S, the type of the selector expression:
                  
sealed interface S permits A, B, C { }
final class A implements S { }
final class B implements S { }
record C(int i) implements S { }  // Implicitly final
//...
    static int testSealedCoverage(S s) {
        return switch (s) {
            case A a -> 1;
            case B b -> 2;
            case C c -> 3;
        };
    }The compiler can also determine the type coverage of a
                switch expression or statement if the type of its selector
            expression is a generic sealed class. The following example compiles. The only permitted
            subclasses of interface I are classes A and
                B. However, because the selector expression is of type
                I<Integer>, the switch block requires only
            class B in its type coverage to be exhaustive:
                  
    sealed interface I<T> permits A, B {}
    final class A<X> implements I<String> {}
    final class B<Y> implements I<Y> {}
    static int testGenericSealedExhaustive(I<Integer> i) {
        return switch (i) {
        // Exhaustive as no A case possible!  
            case B<Integer> bi -> 42;
        };
    }The type of a switch expression or statement's selector
            expression can also be a generic record. As always, a switch expression
            or statement must be exhaustive. The following example doesn't compile. No match for a
                Pair exists that contains two values, both of type
                A:
                  
record Pair<T>(T x, T y) {}
class A {}
class B extends A {}
static void notExhaustive(Pair<A> p) {
    switch (p) {                 
        // error: the switch statement does not cover all possible input values
        case Pair<A>(A a, B b) -> System.out.println("Pair<A>(A a, B b)");
        case Pair<A>(B b, A a) -> System.out.println("Pair<A>(B b, A a)");
    }        
}The following example compiles. Interface I is sealed. Types C and D cover all possible instances:
record Pair<T>(T x, T y) {}
sealed interface I permits C, D {}
record C(String s) implements I {}
record D(String s) implements I {}
// ...
static void exhaustiveSwitch(Pair<I> p) {
    switch (p) {
        case Pair<I>(I i, C c) -> System.out.println("C = " + c.s());
        case Pair<I>(I i, D d) -> System.out.println("D = " + d.s());
    }
}If a switch expression or statement is exhaustive at
            compile time but not at run time, then a MatchException is
            thrown. This can happen when a class that contains an exhaustive switch
            expression or statement has been compiled, but a sealed hierarchy that is used in the
            analysis of the switch expression or statement has been subsequently
            changed and recompiled. Such changes are migration incompatible and may lead to a
                MatchException being thrown when running the
                switch statement or expression. Consequently, you need to recompile
            the class containing the switch expression or statement.
                  
Consider the following class PrintA and sealed interface
                OnlyAB:
                  
class PrintA {
    public static void main(String[] args) {
        System.out.println(switch (OnlyAB.getAValue()) {
            case A a -> 1;
            case B b -> 2;
        });
    }
}sealed interface OnlyAB permits A, B {
    static OnlyAB getAValue() {
        return new A();
    }
}
final class A implements OnlyAB {}
final class B implements OnlyAB {}The switch expression in the class PrintA
            is exhaustive and this example compiles. When you run PrintA, it prints
            the value 1. However, suppose you edit OnlyAB as
            follows and compile this interface and not
                     PrintA:
                  
sealed interface OnlyAB permits A, B, C {
    static OnlyAB getAValue() {
        return new A();
    }
}
final class A implements OnlyAB {}
final class B implements OnlyAB {}
final class C implements OnlyAB {}When you run PrintA, it throws a MatchException:
                  
Exception in thread "main" java.lang.MatchException
            at PrintA.main(PrintA.java:3)Completion and switch Expressions
A switch expression must either complete normally with a
        value or complete abruptly by throwing an exception
                  
A. For example, the following code doesn't compile because the
                switch labeled rule doesn't contain a yield
            statement:
                  
int i = switch (day) {
    case MONDAY -> {
        System.out.println("Monday"); 
        // error: block doesn't contain a yield statement
    }
    default -> 1;
};The following example doesn't compile because the switch
            labeled statement group doesn't contain a yield statement:
                  
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
};Because 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:
                  
z: 
    for (int i = 0; i < MAX_VALUE; ++i) {
        int k = switch (e) { 
            case 0:  
                yield 1;
            case 1:
                yield 2;
            default: 
                continue z; 
                // error: illegal jump through a switch expression 
        };
    // ...
    }