Why is default required for a switch on an enum in

2019-02-07 16:55发布

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

6条回答
乱世女痞
2楼-- · 2019-02-07 17:22

As has been stated, you need to return a value and the compiler doesn't assume that the enum cannot change in the future. E.g. you can create another version of the enum and use that without recompiling the method.

Note: there is a third value for xyz which is null.

public static String testSwitch(XYZ xyz) {
    if(xyz == null) return "null";
    switch(xyz){
    case A:
        return "A";
    case B:
        return "B";
    }
    return xyz.getName();
}

This ha the same result as

public static String testSwitch(XYZ xyz) {
     return "" + xyz;
}

The only way to avoid a return is to throw an exception.

public static String testSwitch(XYZ xyz) {
    switch(xyz){
    case A:
        return "A";
    case B:
        return "B";
    }
    throw new AssertionError("Unknown XYZ "+xyz);
}
查看更多
ら.Afraid
3楼-- · 2019-02-07 17:26

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.

查看更多
甜甜的少女心
4楼-- · 2019-02-07 17:30

Because compiler cannot guess that there are only two values in the enum and forces you to return value from the method. (However I dont know why it cannot guess, maybe it has something with reflection).

查看更多
做自己的国王
5楼-- · 2019-02-07 17:31

There is a contract that this method has to return a String unless it throws an Exception. And everytime is not limited to those cases where the value of xyz is equal to XVZ.A or XYZ.B.

Here's another example, where it's obviuos, that the code will run correct but where we have a compiletime error for the very same reason:

public boolean getTrue() {
  if (1 == 1) return true;
}

It is not true that you have to add a default statement, it is true, that you have to return a value at any time. So either add a default statement or add a return statement after the switch block.

查看更多
祖国的老花朵
6楼-- · 2019-02-07 17:41

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.

查看更多
你好瞎i
7楼-- · 2019-02-07 17:41
default: throw new AssertionError();
查看更多
登录 后发表回答