StatementSwitchToExpressionSwitch
This statement switch can be converted to a new-style arrow switch

Severity
WARNING

The problem

We’re trying to make switches simpler to understand at a glance. Misunderstanding the control flow of a switch is a common source of bugs.

As part of this simplification, new-style arrow (->) switches are encouraged instead of old-style colon (:) switches. And where possible, neighboring cases are grouped together (e.g. case A, B, C).

Old-style colon (:) switches:

New-style arrow (->) switches:

Examples

1. Eliminate fall through

enum 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 by grouping and using a new-style 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"); } } } 

2. return switch ...

Sometimes switch is used with a return for each case, like this:

enum SideOfCoin {OBVERSE, REVERSE}; private String renderName(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"); } 

Note that even though a case is present for each possible value of the enum, a boilerplate “should never happen” clause is still needed. The transformed code is simpler and doesn’t need a “should never happen” clause.

enum SideOfCoin {OBVERSE, REVERSE}; private String renderName(SideOfCoin sideOfCoin) { return switch(sideOfCoin) { case OBVERSE -> "Heads"; case REVERSE -> "Tails"; }; } 

If you nevertheless wish to define an explicit “should never happen” clause, this can be accomplished by placing the logic inside a default case. For example:

enum SideOfCoin {OBVERSE, REVERSE}; private String foo(SideOfCoin sideOfCoin) { return switch(sideOfCoin) { case OBVERSE -> "Heads"; case REVERSE -> "Tails"; default -> throw new RuntimeException("Unknown side of coin"); // should never happen }; } 

When the checker detects an existing default that appears to be redundant, it may suggest a secondary auto-fix which removes the redundant default and its code (if any).

3. Assignment switch

If every branch of a switch is making an assignment to the same variable, the code can be simplified into a combined assignment and 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; }; } 

Taking this one step further: if a local variable is defined, and then immediately followed by a switch in which every case assigns to that same variable, then all three (the switch, the variable declaration, and the assignment) can be merged:

enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS}; private void updateStatus(Suit suit) { int score; switch(suit) { case HEARTS: // Fall thru case DIAMONDS: score = 1; break; case SPADES: score = 2; break; case CLUBS: score = 3; } ... } 

Becomes:

enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS}; private void updateStatus(Suit suit) { int score = switch(suit) { case HEARTS, DIAMONDS -> 1; case SPADES -> 2; case CLUBS -> 3; }; ... } 

4. Just converting to new arrow switch

Even when the simplifications discussed above are not applicable, conversion to new arrow switches can be automated by this checker:

enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS}; private void processEvent(Suit suit) { switch (suit) { case CLUBS: String message = "hello"; var anotherMessage = "salut"; processMessages(message, anotherMessage); break; case DIAMONDS: anotherMessage = "bonjour"; processMessage(anotherMessage); } } 

Note that the local variables referenced in multiple cases are hoisted up out of the switch statement, and var declarations are converted to explicit types, resulting in:

enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS}; private void processEvent(Suit suit) { String anotherMessage; switch (suit) { case CLUBS -> { String message = "hello"; anotherMessage = "salut"; processMessages(message, anotherMessage); } case DIAMONDS -> { anotherMessage = "bonjour"; processMessage(anotherMessage); } } } 

5. Complex control flows

Here’s an example of a complex statement switch with conditional fall-through and various control flows. Unfortunately, the checker does not currently have the ability to automatically convert such code to new-style arrow switches. Manually converting the code could be a good opportunity to improve its readability.

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; } 

Suppression

Suppress false positives by adding the suppression annotation @SuppressWarnings("StatementSwitchToExpressionSwitch") to the enclosing element.