File: UnnecessaryDefaultInEnumSwitch.md

package info (click to toggle)
error-prone-java 2.4.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 15,076 kB
  • sloc: java: 171,398; xml: 1,459; sh: 34; makefile: 7
file content (148 lines) | stat: -rw-r--r-- 4,213 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
Including a default case is redundant when switching on an enum type if the
switch handles all possible values of the enum, and execution cannot continue
below the switch from any of the non-default statement groups.

TIP: Removing the unnecessary default allows Error Prone to enforce that the
switch continues to handle all cases, even if new values are added to the enum,
see: [MissingCasesInEnumSwitch](MissingCasesInEnumSwitch.md). After the
unnecessary default is removed, Error Prone will report an error if new enum
constants are added in the future, to remind you to either handle the cases
explicitly or restore the default case.

## When the default can be removed

This check does not report cases where execution can continue after the switch
statement from any non-default statement groups. For example, consider:

```java
enum TrafficLightColour { RED, GREEN, YELLOW }

void approachIntersection(TrafficLightColour state) {
  boolean stop;
  switch (state) {
    case GREEN:
      stop = false;
      break;
    case YELLOW:
    case RED:
      stop = true;
      break;
  }
  if (stop) {
    ...
  }
}
```

The definition of control flow in [JLS §14.21] does not consider whether enum
switches handle all cases, so in the example above javac will complain that
`stop` is not definitely assigned. This is because adding constants to an enum
is a binary compatible change (see [JLS §13.4.26]), so the spec allows for the
possibility that `TrafficLightColour` is defined in another library, and after
compiling our code we update to a new version of the library (without
recompiling) that adds another colour of traffic light (say, `PURPLE`) that the
switch doesn't handle.

This check should be used together with `MissingCasesInEnumSwitch` in
environments where that kind of binary incompatibility is very unlikely. For
example, if your build system accurately tracks changes to dependencies and you
are deploying an application (instead of a library), the risk of skew between
compile-time and runtime is minimal. One the other hand, if you are a library
author and your code switches on an enum in a different library, you want want
to include 'defensive' default cases to handle the situation where a user
deploys your code together with an incompatible version of the other library.

[JLS §14.21]: https://docs.oracle.com/javase/specs/jls/se10/html/jls-14.html#jls-14.21
[JLS §13.4.26]: https://docs.oracle.com/javase/specs/jls/se10/html/jls-13.html#jls-13.4.26

## Examples

The following examples show situations where it is usually safe to remove the
default from an exhaustive enum switch.

### All cases return or throw

Before:

```java
enum State { ON, OFF }

boolean isOn(State state) {
  switch (state) {
    case ON:
      return true;
    case OFF:
      return false;
    default:
      throw new AssertionError("unknown state: " + state);
  }
}
```

After:

```java
enum State { ON, OFF }

boolean isOn(State state) {
  switch (state) {
    case ON:
      return true;
    case OFF:
      return false;
  }
  throw new AssertionError("unknown state: " + state);
}
```

### The default case is empty

Before:

```java
enum State { ON, OFF }

boolean isOn(State state) {
  switch (state) {
    case ON:
      return true;
    case OFF:
      break;
    default:
      break;
  }
  return false;
}
```

After:

```java
enum State { ON, OFF }

boolean isOn(State state) {
  switch (state) {
    case ON:
      return true;
    case OFF:
      break;
  }
  return false;
}
```

## Cases with UNRECOGNIZED

When a switch statement handles all values of a proto-generated enum except for
UNRECOGNIZED, the UNRECOGNIZED case should be explicitly handled and the default
should be removed. This is preferred so that `MissingCasesInEnumSwitch` will
catch unexpected enum types at compile-time instead of runtime.

If the switch statement cannot [complete normally], the default should be
deleted and its statements moved after the switch statement. The UNRECOGNIZED
case should be added with a break.

If it can complete normally, the default should be merged with an added
UNRECOGNIZED case.

[complete normally]: https://docs.oracle.com/javase/specs/jls/se10/html/jls-14.html#jls-14.1