如何在开关中使用null


202
Integer i = ...

switch (i){
    case null:
        doSomething0();
        break;    
    }

在上面的代码中,我不能在switch case语句中使用null。我该怎么做呢?我无法使用,default因为那之后我想做点其他的事情。


9
切换之前检查空条件if(i == null){//
dosomething

8
这实际上将使该开关有用。其他模式匹配语言以这种方式工作。
Pyrolistical

Answers:


276

使用switchJava中的语句无法做到这一点。在null之前检查switch

if (i == null) {
    doSomething0();
} else {
    switch (i) {
    case 1:
        // ...
        break;
    }
}

您不能在switch语句*中使用任意对象。究其原因,编译器不会抱怨switch (i)这里i是一个Integer是因为Java自动unboxes的Integer一个int。正如assylias已经说过的,拆箱将抛出NullPointerExceptionwhen iis null

*从Java 7开始,您可以String在in switch语句中使用。

Oracle Docs中的更多信息switch(包括带有null变量的示例)-Switch


16
您还可以在switch语句中使用枚举。
joriki 2013年

27
有道理的是,由于取消装箱,您不能使用空的Integer或其他Wrapper类。但是枚举和字符串呢?为什么它们不能为空?
Luan Nico

9
我不明白为什么没有为字符串实现空短路到“默认”情况或空开关的特殊情况。由于始终必须进行空检查,因此使用开关简化代码毫无意义。我并不是说简化是开关的唯一用途。
Reimius 2014年

3
@Reimius您不必总是执行null检查。如果您遵守为您的方法提供的代码协定,那么几乎总是可以设法避免因空检查而使代码混乱。但是,使用断言总是很不错的。
乔佛里

我也想知道@LuanNico查询的答案。null使用Stringenum类型时,这不可能是有效的情况,这似乎是不合理的。也许enum实现依赖于ordinal()在幕后进行调用(尽管如此,为什么不将null“序数”视为-1?),并且该String版本使用intern()和指针比较进行某些操作(或者依赖于严格要求取消引用的某些操作)。目的)?
aroth '16


40

switch(i)将抛出一个NullPointerException,如果我是null,因为它会尝试拆箱Integerint。因此case null,碰巧是非法的,无论如何都不会达到。

您需要在switch声明之前检查我是否不为null 。


23

Java文档明确指出:

禁止将null用作开关标签的禁令会阻止人们编写永远无法执行的代码。如果switch表达式是引用类型,例如装箱的原始类型或枚举,则如果表达式在运行时评估为null,则将发生运行时错误。

您必须在执行Swithch语句之前验证null。

if (i == null)

请参阅switch语句

case null: // will never be executed, therefore disallowed.

1
您链接上的javadocs不再说“禁止将null用作开关标签[etc.]”。
Patrick M


14

鉴于:

public enum PersonType {
    COOL_GUY(1),
    JERK(2);

    private final int typeId;
    private PersonType(int typeId) {
        this.typeId = typeId;
    }

    public final int getTypeId() {
        return typeId;
    }

    public static PersonType findByTypeId(int typeId) {
        for (PersonType type : values()) {
            if (type.typeId == typeId) {
                return type;
            }
        }
        return null;
    }
}

对我来说,这通常与数据库中的查找表对齐(仅适用于很少更新的表)。

但是,当我尝试findByTypeId在switch语句中使用(很可能是用户输入)时...

int userInput = 3;
PersonType personType = PersonType.findByTypeId(userInput);
switch(personType) {
case COOL_GUY:
    // Do things only a cool guy would do.
    break;
case JERK:
    // Push back. Don't enable him.
    break;
default:
    // I don't know or care what to do with this mess.
}

...正如其他人所说,这将导致NPE @ switch(personType) {。我开始实施的一种解决方法(即“解决方案”)是添加一种UNKNOWN(-1)类型。

public enum PersonType {
    UNKNOWN(-1),
    COOL_GUY(1),
    JERK(2);
    ...
    public static PersonType findByTypeId(int id) {
        ...
        return UNKNOWN;
    }
}

现在,您不必进行空值检查,您可以选择是否处理UNKNOWN类型。(注意:-1在业务场景中是不太可能的标识符,但显然选择对您的用例有意义的内容)。


2
UNKNOWN是我见过的最好的解决方案,可以克服nullchecks。
Membersound's

5

你必须做一个

if (i == null) {
   doSomething0();
} else {
   switch (i) {
   }
}

4

一些库尝试提供内置java switch语句的替代方法。Vavr是其中之一,他们将其推广到模式匹配。

这是他们的文件的一个例子:

String s = Match(i).of(
    Case($(1), "one"),
    Case($(2), "two"),
    Case($(), "?")
);

您可以使用任何谓词,但是它们提供了许多现成的谓词,并且$(null)完全合法。与替代方案相比,我发现这是一种更优雅的解决方案,但这需要java8和对vavr库的依赖...



2
switch (String.valueOf(value)){
    case "null":
    default: 
}

0

你不能 您可以在switch中使用原语(int,char,short,byte)和String(仅Java 7中的Strings)。原语不能为null。切换之前请先
检查其他i条件。


4
您也可以使用枚举。
卡鲁(Karu),

5
如果枚举为空,则将遇到相同的问题。顺便说一句,开关不能处理空值,这很奇怪,因为它有一个默认子句

1
@LeonardoKenji默认子句与null无关。无论您打开什么,它都会被取消引用以便检查其他任何情况,因此default子句将不会处理null情况(在有机会抛出NullPointerException之前)。

2
我认为他的意思是,默认子句应将null当作其他可能的枚举值处理,而上一个案例没有捕获该枚举值
Leo

0

只要考虑一下SWITCH的工作方式,

  • 如果是原语,我们知道它可能会因NPE自动装箱而失败
  • 但是对于Stringenum,它可能正在调用equals方法,该方法显然需要在其上调用equals的LHS值。因此,由于不能在null上调用任何方法,因此switch不能处理null。

0

基于@tetsuo答案,使用Java 8:

Integer i = ...

switch (Optional.ofNullable(i).orElse(DEFAULT_VALUE)) {
    case DEFAULT_VALUE:
        doDefault();
        break;    
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.