It is good practice to ensure that each switch
statement should be complete
in the sense that each execution at run time will find a suitable branch.
This is actually a non-trivial task when taking all of the following into consideration:
Additionally, the JLS mandates a flow analysis for enum switch statements which can lead to unexpected compile time errors:
enum Colors { RED, GREEN, BLUE } String colorString(Color c) { switch(c) { case RED: return "red"; case GREEN: return "green"; case BLUE: return "blue"; } }
The compiler will answer:
"This method must return a result of type String. Note that a problem regarding missing 'default:' on 'switch' has been suppressed, which is perhaps related to this problem".
This message has been designed to alert users of the different notions of completeness: the flow analysis mandated by the JLS considers each enum switch statement without a default case as incomplete, even if it lists all (currently known) enum constants. This concerns return statements as well as definite assignment of local variables or blank final fields. However, if you follow all recommendations below, the above error message will never occur.
Please consult the compiler preferences regarding the individual configuration options for reporting various levels of incompleteness. In the sequel we discuss different design goals and policies and how they can be checked using the Eclipse Java compiler.
Optimally, each switch statement should have a meaningful default branch, which handles all cases that are not explicitly listed using an "always-reasonable" strategy. Obviously, such a strategy is difficult or even impossible to find in many cases, but if you can find a meaningful default implementation, that's certainly the best solution.
If the compiler warns you about a missing default case, but the reasonable thing to do by default is just do nothing, this might as well be worth documenting.
default:
label whose action is only a comment explaining that (and why)
doing nothing is OK in this switch. By doing so you also tell the compiler that you did not
simply forget about a default case.
If definitely no reasonable default implementation can be found, as a last resort you may want to prevent an unexpected value to be the root cause for other errors further downstream, i.e., you may want to fail early at runtime.
default:
case that throws
an exception and/or logs the problem.
Each switch statement should be assignable to one of the three above categories. This means letting the compiler warn you about each missing default case is a universally valid strategy.
When performing a switch over an enum value, it may be desirable to explicitly cover each enum constant by a corresponding case statement.
If you don't like the option that all enum switches should mention all enum constants, but still want to get the warning about missing case labels for some enum switch statements, you'll need to select the strategy for each individual enum switch statement.
String colorString2(Color c) { switch(c) { case RED: return "red"; case GREEN: return "green"; //$CASES-OMITTED$ default: return "unknown color"; } }
Hint: If the compiler reports something like "The enum constant BLUE should have a corresponding case label in this enum switch on Color" a quick fix will be offered for inserting the tag comment.
The above considerations have shown that you get most help from the compiler if you enable all optional warnings
regarding incomplete switch statements, and use empty documented default cases plus //$CASES-OMITTED$
tag comments
to mark those switch statements where incompleteness is OK by design.
If you follow these recommendations, you will get all relevant warnings (or errors if you like)
when branches are accidentally omitted, including the situation of late addition of one or more enum constants.
All exceptions from this maximum completeness will be documented in the code and no longer flagged with a warning.
Still the compiler cannot prevent the use of inconsistent class versions at runtime, so a class may be compiled against an old version of an enum type, but in the running application the enum type may have more enum constants. For these and similar situations an appropriate "catch-all" default implementations can raise runtime exceptions to alert of this inconsistency and prevent the error from propagating further into the application.
As an added value of the strict compiler settings recommended above, the dubious error message about a missing return statement shown above (or similar messages about uninitialized variables) will no longer occur.