154

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.

14
  • 14
    No conclusive answer to this, since we're not the people who made the language. All answers will be pure conjecture. Commented Aug 15, 2013 at 23:22
  • 2
    An attempt to switch on null will cause an exception. Perform an if check for null, then go into the switch statement. Commented Aug 15, 2013 at 23:22
  • 2
    Re: conjecture: It has taken more than ten years for this feature to land. I am sure there is ample discussion to be found about the reasoning behind the implementation that was finally chosen. Commented Aug 15, 2013 at 23:24
  • 1
    Speaking of which, this reminds me of an old bug in Eclipse that would execute the default case on null being passed into it, whereas the specification says that it should throw an NPE. Commented Aug 15, 2013 at 23:25
  • 7
    @JeffGohlke: "There is no way to answer a why question unless you are the person who made the decision."... well, gparyani's comment proves otherwise Commented Aug 15, 2013 at 23:51

10 Answers 10

167

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.

Sign up to request clarification or add additional context in comments.

3 Comments

It would have been a big improvment to allow null handling as part of a case null: if it would have been implemented exclusivily for String. Currently all String checking requires a null-check anyhow if we want to do it correct, although mostly implicitly by putting the string constant first as in "123test".equals(value). Now we are forced to write our switch statement as in if (value != null) switch (value) {...
Re "mapping null to 0 wouldn't be a good idea": that's an understatement since the value of "".hashcode() is 0! It would mean that a null string and a zero length string would have to be treated identically in a switch statement, which is clearly not viable.
For the case of enum, what was stopping them from mapping null to -1?
39

In general null is nasty to handle; maybe a better language can live without null.

Your problem might be solved by

 switch(month==null?"":month) { ... //case "": default: monthNumber = 0; } 

2 Comments

Not a good idea if month is an empty string: this will treat it the same way as a null string.
For many cases treating null as empty string can be perfectly reasonable
31

It isn't pretty, but String.valueOf() allows you to use a null String in a switch. If it finds null, it converts it to "null", otherwise it just returns the same String you passed it. If you don't handle "null" explicitly, then it will go to default. The only caveat is that there is no way of distinguishing between the String "null" and an actual null variable.

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

2 Comments

I believe doing something like this in java is an antipattern.
@ŁukaszRzeszotarski That's pretty much what I meant by "it isn't pretty"
22

This is an attempt to answer why it throws NullPointerException

The output of the javap command below reveals that case is chosen based on the hashcode of the switch argument string and hence throws NPE when .hashCode() is invoked on null string.

6: invokevirtual #18 // Method java/lang/String.hashCode:()I 9: lookupswitch { // 3 -1826660246: 44 -263893086: 56 103666243: 68 default: 95 } 

This means, based on answers to Can Java's hashCode produce same value for different strings?, though rare, there is still a possibility of two cases being matched (two strings with same hash code) See this example below

 int monthNumber; String month = args[0]; switch (month) { case "Ea": monthNumber = 1; break; case "FB": monthNumber = 2; break; // case null: default: monthNumber = 0; break; } System.out.println(monthNumber); 

javap for which

 10: lookupswitch { // 1 2236: 28 default: 59 } 28: aload_3 29: ldc #22 // String Ea 31: invokevirtual #24 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 34: ifne 49 37: aload_3 38: ldc #28 // String FB 40: invokevirtual #24 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 43: ifne 54 46: goto 59 //Default 

As you can see only one case gets generated for "Ea" and "FB" but with two if conditions to check for a match with each case string. Very interesting and complicated way of implementing this functionality!

3 Comments

That could have been implemented in another way, though.
I'd say this is a design bug.
I wonder if this ties in with why some dubious aspects of the string hash function haven't been changed: in general code shouldn't rely upon hashCode returning the same values on different runs of a program, but since string hashes get baked into executables by the compiler, the string hash method ends up being part of the language spec.
9

Long story short ... (and hopefully interesting enough!!!)

Enum were first introduced in Java1.5 (Sep'2004) and the bug requesting to allow switch on String was filed long back (Oct'95). If you look at the comment posted on that bug at Jun'2004, it says Don't hold your breath. Nothing resembling this is in our plans. Looks like they deferred (ignored) this bug and eventually launched Java 1.5 in the same year in which they introduced 'enum' with ordinal starting at 0 and decided (missed) not to support null for enum. Later in Java1.7 (Jul'2011) they followed (forced) the same philosophy with String (i.e. while generating the bytecode no null check was performed before calling hashcode() method).

So I think it boils down to the fact enum came in first and was implemented with its ordinal begin at 0 due to which they couldn't support null value in switch block and later with String they decided to forced the same philosophy i.e. null value not allowed in switch block.

TL;DR With String they could have take care of NPE (caused by attempt to generate hashcode for null) while implementing java code to byte code conversion but finally decided not to.

Ref: TheBUG, JavaVersionHistory, JavaCodeToByteCode, SO

Comments

1

According to Java Docs:

A switch works with the byte, short, char, and int primitive data types. It also works with enumerated types (discussed in Enum Types), the String class, and a few special classes that wrap certain primitive types: Character, Byte, Short, and Integer (discussed in Numbers and Strings).

Since null has no type, and is not an instance of anything, it will not work with a switch statement.

2 Comments

And yet null is a valid value for a String, Character, Byte, Short, or Integer reference.
@asteri 7 years later, I'd like to point out that switch statement doesn't compare instance ref values; it compares instance values.
1

From the JLS:

In the judgment of the designers of the Java programming language, [throwing a NullPointerException if the expression evaluates to null at runtime] 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).

- gparyani   Aug 15, 2013 at 23:37

Comments

0

The answer is simply that if you use a switch with a reference type (such as a boxed primitive type), the run-time error will occur if the expression is null because unboxing it would throw the NPE.

so case null (which is illegal) could never be executed anyway ;)

5 Comments

That could have been implemented in another way, though.
OK @Thilo, smarter people than me were involved in this implementation. If you know of other ways in which this could have been implemented, I would love to know what those are [and I'm sure there are others] so do share ...
Strings are not boxed primitive types, and the NPE does not occur because someone is trying to "unbox" them.
@thilo, the other ways to implement this are, what?
if (x == null) { // the case: null part }
0

I agree with insightful comments (Under the hood ....) in https://stackoverflow.com/a/18263594/1053496 in @Paul Bellora's answer.

I found one more reason from my experience.

If 'case' can be null that means switch(variable) is null then as long as the developer provides a matching 'null' case then we can argue it's fine . But what will happen if the developer does not provide any matching 'null' case. Then we have to match it to a 'default' case which may not be what developer has intended to handle in the default case. Therefore matching 'null' to a default could cause 'surprising behaviour'. Therefore throwing 'NPE' will make the developer to handle every cases explicitly. I found throwing NPE in this case very thoughtful.

Comments

0

Use Apache StringUtils class

String month = null; switch (StringUtils.trimToEmpty(month)) { case "xyz": monthNumber=1; break; default: monthNumber=0; break; } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.