Why is default required for a switch on an enum?

后端 未结 8 2018
礼貌的吻别
礼貌的吻别 2020-11-29 08:46

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

相关标签:
8条回答
  • 2020-11-29 09:23

    In Java 12 you can use the preview switch expression feature (JEP-325) as follows:

    public static String testSwitch(XYZ xyz) {
        return switch (xyz) {
            case A -> "A";
            case B -> "B";
        };
    }
    

    and you don't need default case as long as you handle all enum values in switch.

    Note, to use a preview feature you'll have to pass --enable-preview --source 12 options to javac and java

    0 讨论(0)
  • 2020-11-29 09:23

    what happens when xyz is null in your code example? In that case the method is missing a return statement.

    0 讨论(0)
  • 2020-11-29 09:26

    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).

    0 讨论(0)
  • 2020-11-29 09:34
    default: throw new AssertionError();
    
    0 讨论(0)
  • 2020-11-29 09:35

    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.)

    0 讨论(0)
  • 2020-11-29 09:41

    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);
    }
    
    0 讨论(0)
提交回复
热议问题