Reputation: 11087
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.
Upvotes: 154
Views: 81374
Reputation: 21073
From the JLS:
In the judgment of the designers of the Java programming language, [throwing a
NullPointerException
if the expression evaluates tonull
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).
Upvotes: 1
Reputation: 55233
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 theswitch
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 tonull
at run time. In the judgment of the designers of the Java programming language, this is a better outcome than silently skipping the entireswitch
statement or choosing to execute the statements (if any) after thedefault
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 toswitch
as well as its cases areint
s. The int value to switch on is determined by invoking the methodEnum.ordinal()
. The [...] ordinals start at zero.That means, mapping
null
to0
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
.
Upvotes: 166
Reputation: 11087
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!
Upvotes: 22
Reputation: 39
Use Apache StringUtils class
String month = null;
switch (StringUtils.trimToEmpty(month)) {
case "xyz":
monthNumber=1;
break;
default:
monthNumber=0;
break;
}
Upvotes: 0
Reputation: 1314
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;
Upvotes: 31
Reputation: 3733
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.
Upvotes: 0
Reputation: 22471
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
Upvotes: 9
Reputation: 19682
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;
}
Upvotes: 40
Reputation: 23503
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.
Upvotes: 1
Reputation: 973
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 ;)
Upvotes: 0