1- We're trying to make ` switch ` statements simpler to understand at a glance.
2- Misunderstanding the control flow of a ` switch ` block is a common source of
3- bugs.
1+ We're trying to make ` switch ` es simpler to understand at a glance.
2+ Misunderstanding the control flow of a ` switch ` is a common source of bugs.
43
5- ### Statement ` switch ` statements:
4+ As part of this simplification, new-style arrow (` -> ` ) switches are encouraged
5+ instead of old-style colon (` : ` ) switches. And where possible, neighboring cases
6+ are grouped together (e.g. ` case A, B, C ` ).
67
7- * Have a colon between the ` case ` and the case's code. For example, `case
8+ ### Old-style colon (` : ` ) ` switch ` es:
9+
10+ * Have a colon between the ` case ` and the ` case ` 's code. For example, `case
811 HEARTS:`
912* Because of the potential for fall-through, it takes time and cognitive load
10- to understand the control flow for each ` case `
11- * When a ` switch ` block is large, just skimming each ` case ` can be toilsome
12- * Fall-though can also be conditional (see example below). In this scenario,
13- one would need to reason about all possible flows for each ` case ` . When
14- conditionally falling-through multiple ` case ` s in a row is possible, the
15- number of potential control flows can grow rapidly
16-
17- ### Expression ` switch ` statements
18-
19- * Have an arrow between the ` case ` and the case's code. For example, `case
13+ to understand the control flow. When a ` switch ` block is large, just
14+ skimming each ` case ` can be toilsome. Fall-through can also be conditional
15+ (see example 5. below). In this scenario, one would potentially need to
16+ reason about all possible flows for each ` case ` . When conditionally
17+ falling-through multiple ` case ` s, the number of potential control flows can
18+ grow rapidly
19+ * Lexical scopes overlap, which can lead to surprising behaviors: definitions
20+ of local variables from earlier ` case ` s are propagated down to later
21+ ` case ` s, however the * values* that initialize those local variables do not
22+ propagate in the same way
23+
24+ ### New-style arrow (` -> ` ) ` switch ` es:
25+
26+ * Have an arrow between the ` case ` and the ` case ` 's code. For example, `case
2027 HEARTS ->`
21- * With an expression ` switch ` statement, you know at a glance that no cases
22- fall through. No control flow analysis needed
28+ * No ` case ` s fall through; no control flow analysis needed
2329* Safely and easily reorder ` case ` s (within a ` switch ` )
24- * It's also possible to group identical cases together ( ` case A, B, C ` ) for
25- improved readability
30+ * Lexical scopes are isolated between different ` case ` s; if you define a local
31+ variable within a ` case ` , it can only be used within that specific ` case ` .
2632
2733### Examples
2834
@@ -48,7 +54,7 @@ private void foo(Suit suit) {
4854}
4955```
5056
51- Which can be simplified into the following expression ` switch ` :
57+ Which can be simplified by grouping and using a new-style switch:
5258
5359``` {.good}
5460enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};
@@ -65,16 +71,14 @@ private void foo(Suit suit) {
6571}
6672```
6773
68- #### 2. Return switch
74+ #### 2. ` return switch ... `
6975
70- Sometimes ` switch ` is used with ` return ` . Below, even though a ` case ` is
71- specified for each possible value of the ` enum ` , note that we nevertheless need
72- a "should never happen" clause:
76+ Sometimes ` switch ` is used with a ` return ` for each ` case ` , like this:
7377
7478``` {.bad}
7579enum SideOfCoin {OBVERSE, REVERSE};
7680
77- private String foo (SideOfCoin sideOfCoin) {
81+ private String renderName (SideOfCoin sideOfCoin) {
7882 switch(sideOfCoin) {
7983 case OBVERSE:
8084 return "Heads";
@@ -86,43 +90,45 @@ private String foo(SideOfCoin sideOfCoin) {
8690}
8791```
8892
89- Using an expression switch simplifies the code and removes the need for an
90- explicit "should never happen" clause.
93+ Note that even though a ` case ` is present for each possible value of the ` enum ` ,
94+ a boilerplate "should never happen" clause is still needed. The transformed code
95+ is simpler and doesn't need a "should never happen" clause.
9196
9297```
9398enum SideOfCoin {OBVERSE, REVERSE};
9499
95- private String foo (SideOfCoin sideOfCoin) {
100+ private String renderName (SideOfCoin sideOfCoin) {
96101 return switch(sideOfCoin) {
97102 case OBVERSE -> "Heads";
98103 case REVERSE -> "Tails";
99104 };
100105}
101106```
102107
103- If you nevertheless wish to have an explicit "should never happen" clause, this
104- can be accomplished by placing the logic under a ` default ` case. For example:
108+ If you nevertheless wish to define an explicit "should never happen" clause,
109+ this can be accomplished by placing the logic inside a ` default ` case. For
110+ example:
105111
106112```
107-
108113enum SideOfCoin {OBVERSE, REVERSE};
109114
110115private String foo(SideOfCoin sideOfCoin) {
111116 return switch(sideOfCoin) {
112117 case OBVERSE -> "Heads";
113118 case REVERSE -> "Tails";
114- default -> {
115- // This should never happen
116- throw new RuntimeException("Unknown side of coin");
117- }
119+ default -> throw new RuntimeException("Unknown side of coin"); // should never happen
118120 };
119121}
120122```
121123
122- #### 3. Assignment switch
124+ When the checker detects an existing ` default ` that appears to be redundant, it
125+ may suggest a secondary auto-fix which removes the redundant ` default ` and its
126+ code (if any).
127+
128+ #### 3. Assignment ` switch `
123129
124- If every branch of a ` switch ` is making an assignment to the same variable, it
125- can be re-written as an assignment switch:
130+ If every branch of a ` switch ` is making an assignment to the same variable, the
131+ code can be simplified into a combined assignment and ` switch ` :
126132
127133``` {.bad}
128134enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};
@@ -157,14 +163,107 @@ private void updateScore(Suit suit) {
157163 case HEARTS, DIAMONDS -> -1;
158164 case SPADES -> 2;
159165 case CLUBS -> 3;
160- };
166+ };
167+ }
168+ ```
169+
170+ Taking this one step further: if a local variable is defined, and then
171+ immediately followed by a ` switch ` in which every ` case ` assigns to that same
172+ variable, then all three (the ` switch ` , the variable declaration, and the
173+ assignment) can be merged:
174+
175+ ``` {.bad}
176+ enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};
177+
178+ private void updateStatus(Suit suit) {
179+ int score;
180+
181+ switch(suit) {
182+ case HEARTS:
183+ // Fall thru
184+ case DIAMONDS:
185+ score = 1;
186+ break;
187+ case SPADES:
188+ score = 2;
189+ break;
190+ case CLUBS:
191+ score = 3;
192+ }
193+ ...
194+
195+ }
196+ ```
197+
198+ Becomes:
199+
200+ ```
201+ enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};
202+
203+ private void updateStatus(Suit suit) {
204+ int score = switch(suit) {
205+ case HEARTS, DIAMONDS -> 1;
206+ case SPADES -> 2;
207+ case CLUBS -> 3;
208+ };
209+ ...
161210}
162211```
163212
164- #### 4. Complex control flows
213+ #### 4. Just converting to new arrow ` switch `
214+
215+ Even when the simplifications discussed above are not applicable, conversion to
216+ new arrow ` switch ` es can be automated by this checker:
217+
218+ ``` {.bad}
219+ enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};
220+
221+ private void processEvent(Suit suit) {
222+ switch (suit) {
223+ case CLUBS:
224+ String message = "hello";
225+ var anotherMessage = "salut";
226+ processMessages(message, anotherMessage);
227+ break;
228+ case DIAMONDS:
229+ anotherMessage = "bonjour";
230+ processMessage(anotherMessage);
231+ }
232+ }
233+ ```
234+
235+ Note that the local variables referenced in multiple cases are hoisted up out of
236+ the ` switch ` statement, and ` var ` declarations are converted to explicit types,
237+ resulting in:
238+
239+ ```
240+ enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};
241+
242+ private void processEvent(Suit suit) {
243+ String anotherMessage;
244+ switch (suit) {
245+ case CLUBS -> {
246+ String message = "hello";
247+ anotherMessage = "salut";
248+ processMessages(message, anotherMessage);
249+ }
250+ case DIAMONDS -> {
251+ anotherMessage = "bonjour";
252+ processMessage(anotherMessage);
253+ }
254+ }
255+ }
256+ ```
257+
258+ #### 5. Complex control flows
165259
166260Here's an example of a complex statement ` switch ` with conditional fall-through
167- and complex control flows. How many potential execution paths can you spot?
261+ and various control flows. Unfortunately, the checker does not currently have
262+ the ability to automatically convert such code to new-style arrow ` switch ` es.
263+ Manually converting the code could be a good opportunity to improve its
264+ readability.
265+
266+ How many potential execution paths can you spot?
168267
169268``` {.bad}
170269enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};
0 commit comments