We're trying to make switch statements simpler to understand at a glance. Misunderstanding the control flow of a switch block is a common source of bugs.
switch statements:case and the case's code. For example, case HEARTS:caseswitch block is large, just skimming each case can be toilsomecase. When conditionally falling-through multiple cases in a row is possible, the number of potential control flows can grow rapidlyswitch statementscase and the case's code. For example, case HEARTS ->switch statement, you know at a glance that no cases fall through. No control flow analysis neededcases (within a switch)case A, B, C) for improved readabilityenum Suit {HEARTS, CLUBS, SPADES, DIAMONDS}; private void foo(Suit suit) { switch(suit) { case HEARTS: System.out.println("Red hearts"); break; case DIAMONDS: System.out.println("Red diamonds"); break; case SPADES: // Fall through case CLUBS: bar(); System.out.println("Black suit"); } }
Which can be simplified into the following expression switch:
enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS}; private void foo(Suit suit) { switch(suit) { case HEARTS -> System.out.println("Red hearts"); case DIAMONDS -> System.out.println("Red diamonds"); case SPADES, CLUBS -> { bar(); System.out.println("Black suit"); } } }
Sometimes switch is used with return. Below, even though a case is specified for each possible value of the enum, note that we nevertheless need a “should never happen” clause:
enum SideOfCoin {OBVERSE, REVERSE}; private String foo(SideOfCoin sideOfCoin) { switch(sideOfCoin) { case OBVERSE: return "Heads"; case REVERSE: return "Tails"; } // This should never happen, but removing this will cause a compile-time error throw new RuntimeException("Unknown side of coin"); }
Using an expression switch simplifies the code and removes the need for an explicit “should never happen” clause.
enum SideOfCoin {OBVERSE, REVERSE};
private String foo(SideOfCoin sideOfCoin) {
return switch(sideOfCoin) {
case OBVERSE -> "Heads";
case REVERSE -> "Tails";
};
}
If you nevertheless wish to have an explicit “should never happen” clause, this can be accomplished by placing the logic under a default case. For example:
enum SideOfCoin {OBVERSE, REVERSE}; private String foo(SideOfCoin sideOfCoin) { return switch(sideOfCoin) { case OBVERSE -> "Heads"; case REVERSE -> "Tails"; default -> { // This should never happen throw new RuntimeException("Unknown side of coin"); } }; }
If every branch of a switch is making an assignment to the same variable, it can be re-written as an assignment switch:
enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS}; int score = 0; private void updateScore(Suit suit) { switch(suit) { case HEARTS: // Fall thru case DIAMONDS: score += -1; break; case SPADES: score += 2; break; case CLUBS: score += 3; } }
This can be simplified as follows:
enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};
int score = 0;
private void updateScore(Suit suit) {
score += switch(suit) {
case HEARTS, DIAMONDS -> -1;
case SPADES -> 2;
case CLUBS -> 3;
};
}
Here's an example of a complex statement switch with conditional fall-through and complex control flows. How many potential execution paths can you spot?
enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS}; private int foo(Suit suit){ switch(suit) { case HEARTS: if (bar()) { break; } // Fall through case CLUBS: if (baz()) { return 1; } else if (baz2()) { throw new AssertionError(...); } // Fall through case SPADES: // Fall through case DIAMONDS: return 0; } return -1; }