Java 为什么';t字符串开关语句是否支持空值?
我只是想知道为什么Java7Java 为什么';t字符串开关语句是否支持空值?,java,switch-statement,language-design,Java,Switch Statement,Language Design,我只是想知道为什么Java7switch语句不支持null大小写,而是抛出NullPointerException?请参见下面的注释行(示例取自): 这将避免在每次使用开关之前出现if空检查的情况。如注释中的damryfbfnetsi,有以下注释: 禁止将null用作开关标签可防止编写无法执行的代码。如果开关表达式属于引用类型,即字符串或装箱基元类型或枚举类型,则如果表达式在运行时的计算结果为null,则会发生运行时错误根据Java编程语言的设计者的判断,这比无声地跳过整个开关语句或选择在默认标
switch
语句不支持null
大小写,而是抛出NullPointerException
?请参见下面的注释行(示例取自):
这将避免在每次使用开关之前出现if
空检查的情况。如注释中的damryfbfnetsi,有以下注释:
禁止将null
用作开关标签可防止编写无法执行的代码。如果开关
表达式属于引用类型,即字符串
或装箱基元类型或枚举类型,则如果表达式在运行时的计算结果为null
,则会发生运行时错误根据Java编程语言的设计者的判断,这比无声地跳过整个开关
语句或选择在默认
标签(如果有的话)后执行语句(如果有的话)要好得多。
(强调矿山)
虽然最后一句跳过了使用case null:
的可能性,但它似乎是合理的,并且提供了语言设计者意图的视图
如果我们更愿意看一下实现细节,Christian Hujer对为什么开关中不允许使用null
(尽管它集中在enum
开关上,而不是String
开关上)有一些深刻的推测:
在引擎盖下,switch
语句通常会编译为tableswitch字节码。开关的“物理”参数及其大小写是int
s。要打开的int值是通过调用方法Enum.ordinal()
确定的。[…]序数从零开始
这意味着,将null
映射到0
不是一个好主意。第一个枚举值上的开关与null不可区分。也许从1开始计算枚举的序数是个好主意。但是它并没有这样定义,这个定义是不能改变的
当String
开关切换时,enum
开关首先出现,并为在引用为null
时切换引用类型的行为开创了先例。答案很简单,如果您将开关与引用类型一起使用(例如装箱的基元类型),如果表达式为null,则会发生运行时错误,因为取消装箱会引发NPE
所以无论如何都不能执行case null(这是非法的;) 根据Java文档:
一个开关可以处理byte、short、char和int原语数据
类型。它也适用于枚举类型(在枚举类型中讨论),
String类,以及一些包装特定
基本类型:字符、字节、短字符和整数(在中讨论
数字和字符串)
由于null
没有类型,并且不是任何内容的实例,因此它将无法与switch语句一起使用。这是一个尝试,旨在回答它抛出NullPointerException
下面javap命令的输出显示,case
是根据开关
参数字符串的hashcode选择的,因此在对空字符串调用.hashcode()
时抛出NPE
6: invokevirtual #18 // Method java/lang/String.hashCode:()I
9: lookupswitch { // 3
-1826660246: 44
-263893086: 56
103666243: 68
default: 95
}
这意味着,根据对的回答,尽管很少,但仍有可能匹配两个案例(两个字符串具有相同的哈希代码),请参见下面的示例
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用于哪个
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
正如您所看到的,只为“Ea”
和“FB”
生成了一个case,但有两个if
条件来检查每个case字符串是否匹配。实现此功能的方式非常有趣且复杂 通常null
很难处理;也许更好的语言可以在没有null
的情况下生存
你的问题可以通过以下方法解决
switch(month==null?"":month)
{
...
//case "":
default:
monthNumber = 0;
}
它并不漂亮,但是String.valueOf()
允许您在开关中使用空字符串。如果它发现null
,它会将其转换为“null”
,否则它只返回与传递它的字符串相同的字符串。如果您没有显式地处理“null”
,那么它将转到默认值。唯一需要注意的是,无法区分字符串“null”
和实际的null
变量
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;
长话短说。。。(希望足够有趣!!!)
Enum最早于(2004年9月)引入,请求允许打开字符串的文件很早就提交了(1995年10月)。如果你在2004年6月的上看到关于这个bug的评论,它说不要屏住呼吸。我们的计划中没有类似的内容。
看起来他们推迟(忽略)了这个错误,并最终在同一年推出了Java 1.5,在这一年中,他们引入了序号为0的“enum”,并决定(错过)不支持null for enum。后来(2011年7月)他们对字符串遵循(强制)相同的原理(即,在生成字节码时,在调用hashcode()方法之前没有执行空检查)
所以我认为这可以归结为这样一个事实:enum是首先出现的,它的序号开始于0,因此它们不能在开关块中支持空值,后来它们决定在字符串中强制使用相同的原理,即开关块中不允许空值
TL;DR使用String,他们可以在实现java代码到字节码的转换时处理NPE(由尝试为null生成哈希代码引起),但最终决定不这样做
参考:
,
,
,
我同意@Paul Bellora的回答中有深刻见解的评论(在引擎盖下…)
我从我的经历中又找到了一个原因
如果“case”可以为null,则表示开关(variabl
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;
String month = null;
switch (StringUtils.trimToEmpty(month)) {
case "xyz":
monthNumber=1;
break;
default:
monthNumber=0;
break;
}