javaswitch-statementlanguage-design

Why doesn't String switch statement support a null case?


I am just wondering why the Java 7 switch statement does not support a null case and instead throws NullPointerException? See the commented line below (example taken from the Java Tutorials article on switch):

{
    String month = null;
    switch (month) {
        case "january":
            monthNumber = 1;
            break;
        case "february":
            monthNumber = 2;
            break;
        case "march":
            monthNumber = 3;
            break;
        //case null:
        default: 
            monthNumber = 0;
            break;
    }

    return monthNumber;
}

This would have avoided an if condition for null check before every switch use.


Solution

  • Oct 2023 Update

    As Mark points out in the comments, as of Java 21 a switch statement does now support case null thanks to JEP 441. A NullPointerException will still be thrown by a switch lacking an explicit case null if null is passed into it.

    Original answer follows:


    As damryfbfnetsi points out in the comments, JLS §14.11 has the following note:

    The prohibition against using null as a switch label prevents one from writing code that can never be executed. If the switch expression is of a reference type, that is, String or a boxed primitive type or an enum type, then a run-time error will occur if the expression evaluates to null at run time. In the judgment of the designers of the Java programming language, this is a better outcome than silently skipping the entire switch statement or choosing to execute the statements (if any) after the default label (if any).

    (emphasis mine)

    While the last sentence skips over the possibility of using case null:, it seems reasonable and offers a view into the language designers' intentions.

    If we rather look at implementation details, this blog post by Christian Hujer has some insightful speculation about why null isn't allowed in switches (although it centers on the enum switch rather than the String switch):

    Under the hood, the switch statement will typically compile to a tablesswitch byte code. And the "physical" argument to switch as well as its cases are ints. The int value to switch on is determined by invoking the method Enum.ordinal(). The [...] ordinals start at zero.

    That means, mapping null to 0 wouldn't be a good idea. A switch on the first enum value would be indistinguishible from null. Maybe it would've been a good idea to start counting the ordinals for enums at 1. However it hasn't been defined like that, and this definition can not be changed.

    While String switches are implemented differently, the enum switch came first and set the precedent for how switching on a reference type should behave when the reference is null.