Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is default required for a switch on an enum?

Normally, default is not necessary in a switch statement. However, in the following situation the code successfully compiles only when I uncomment the default statement. Can anybody explain why?

public enum XYZ {A,B}; public static String testSwitch(XYZ xyz) {     switch(xyz)     {     case A:         return "A";     case B:     //default:         return "B";     } } 
like image 975
apoorv020 Avatar asked Feb 16 '11 06:02

apoorv020


People also ask

Why should a switch statement always have a default?

Reasons to use a default You might also see this when a variable is declared outside the switch-case but not initialized, and each case initializes it to something different. Here the default needs to initialize it too so that down the line code that accesses the variable doesn't raise an error.

Is default mandatory for switch case?

The default statement is optional and can appear anywhere inside the switch block. In case, if it is not at the end, then a break statement must be kept after the default statement to omit the execution of the next case statement.

What is the default of an enum?

The default value for an enum is zero. If an enum does not define an item with a value of zero, its default value will be zero.

Can you use a switch statement for enums?

We can use also use Enum keyword with Switch statement. We can use Enum in Switch case statement in Java like int primitive.


2 Answers

The reason that you have to uncomment the default is that your function says that it returns a String, but if you only have case labels defined for A and B then the function will not return a value if you pass in anything else. Java requires that all functions that state that they return a value actually return a value on all possible control paths, and in your case the compiler isn't convinced that all possible inputs have a value returned.

I believe (and I'm not sure of this) that the reason for this is that even if you cover all your enum cases, the code could still fail in some cases. In particular, suppose that you compile the Java code containing this switch statement (which works just fine), then later on change the enum so that there's now a third constant - let's say C - but you don't recompile the code with the switch statement in it. Now, if you try writing Java code that uses the previously-compiled class and passes in C into this statement, then the code won't have a value to return, violating the Java contract that all functions always return values.

More technically speaking, I think the real reason is that the JVM bytecode verifier always rejects functions in which there is some control path that falls off the end of a function (see §4.9.2 of the JVM spec), and so if the code were to compile it would just get rejected by the JVM at runtime anyway. The compiler therefore gives you the error to report that a problem exists.

like image 109
templatetypedef Avatar answered Sep 20 '22 08:09

templatetypedef


I think this is explained by the JLS definite assignment rules for switch statements (JLS 16.2.9) which states the following:

"V is [un]assigned after a switch statement iff all of the following are true:

  • Either there is a default label in the switch block or V is [un]assigned after the switch expression.

If we then apply this to the notional V which is the return value of the method, we can see that if there is no default branch, the value would be notionally unassigned.

OK ... I'm extrapolating definite assignment rules to cover return values, and maybe they don't. But the fact that I couldn't find something more direct in the spec doesn't mean it isn't there :-)


There's another (more sound) reason why the compiler has to give an error. It stems from the binary compatibility rules for enum (JLS 13.4.26) which state the following:

"Adding or reordering constants from an enum type will not break compatibility with pre-existing binaries."

So how does that apply in this case? Well suppose that the compiler was allowed to infer that the OP's example switch statement always returned something. What happens if the programmer now changes the enum to add an extra constant? According to the JLS binary compatibility rules, we haven't broken binary compatibility. Yet the method containing the switch statement can now (depending on its argument) return an undefined value. That cannot be allowed to happen, so therefore the switch must be a compilation error.


In Java 12 they have introduced enhancements to switch that include switch expressions. This runs into the same problem with enums that change between compile time and runtime. According to the JEP 354, they solving this problem as follows:

The cases of a switch expression must be exhaustive; for all possible values there must be a matching switch label. (Obviously switch statements are not required to be exhaustive.)

In practice this normally means that a default clause is required; however, in the case of an enum switch expression that covers all known constants, a default clause is inserted by the compiler to indicate that the enum definition has changed between compile-time and runtime. Relying on this implicit default clause insertion makes for more robust code; now when code is recompiled, the compiler checks that all cases are explicitly handled. Had the developer inserted an explicit default clause (as is the case today) a possible error will have been hidden.

The only thing that is not crystal clear is what the implicit default clause would actually do. My guess is that it would throw an unchecked exception. (As of right now, the JLS for Java 12 has not been updated to describe the new switch expressions.)

like image 28
Stephen C Avatar answered Sep 24 '22 08:09

Stephen C