【问题标题】:How to use null in switch如何在switch中使用null
【发布时间】:2012-05-07 02:38:01
【问题描述】:
Integer i = ...

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

在上面的代码中,我不能在 switch case 语句中使用 null。我怎样才能做到这一点? 我不能使用default,因为那时我想做其他事情。

【问题讨论】:

  • 在切换前检查空条件 if(i==null) {//dosomething}
  • 这实际上会使开关变得有用。其他模式匹配语言以这种方式工作。

标签: java switch-statement


【解决方案1】:

这对于 Java 中的 switch 语句是不可能的。在switch 之前检查null

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

您不能在switch 语句中使用任意对象*。编译器不抱怨switch (i) 的原因是iInteger,因为Java 自动将Integer 拆箱为int。正如 assylias 已经说过的,当 inull 时,拆箱将抛出 NullPointerException

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

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

【讨论】:

  • 你也可以在 switch 语句中使用枚举。
  • 由于拆箱,您不能使用 null Integer 或其他 Wrapper 类是有道理的。但是枚举和字符串呢?为什么它们不能为空?
  • 我不明白为什么没有为字符串实现将 null 短路映射到“默认”情况或 null 开关的特殊情况。它使使用开关来简化代码毫无意义,因为您总是必须进行空检查。我并不是说简化是开关的唯一用途。
  • @Reimius 你不必总是做空检查。如果你尊重你为你的方法提供的代码契约,你几乎总是可以设法让你的代码杂乱无章地进行空检查。不过,使用断言总是不错的。
  • 我也想知道@LuanNico 问题的答案。在使用 Stringenum 类型时,null 不能成为有效案例似乎是不合理的。也许enum 实现依赖于在幕后调用ordinal()(尽管如此,为什么不将null 视为具有-1 的“序数”?),而String 版本使用intern() 执行某些操作和指针比较(或以其他方式依赖于严格要求取消引用对象的东西)?
【解决方案2】:
switch ((i != null) ? i : DEFAULT_VALUE) {
        //...
}

【讨论】:

  • 比使用一个额外的 if else 更干净的方法
【解决方案3】:

如果 i 是nullswitch(i) 将抛出 NullPointerException,因为它会尝试将 Integer 拆箱成 int。所以case null,碰巧是非法的,无论如何都不会到达。

您需要在switch 语句之前检查 i 不为空。

【讨论】:

    【解决方案4】:

    Java 文档明确指出:

    禁止使用 null 作为开关标签可以防止编写永远无法执行的代码。如果 switch 表达式是引用类型,例如装箱的原始类型或枚举,则如果表达式在运行时计算为 null,则会发生运行时错误。

    您必须在执行 Switch 语句之前验证是否为 null。

    if (i == null)
    

    The Switch Statement

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

    【讨论】:

    【解决方案5】:

    给定:

    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;
        }
    }
    

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

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

    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 在业务场景中不太可能是标识符,但显然选择对您的用例有意义的东西。

    【讨论】:

    • UNKNOWN 是我见过的最好的解决方案,并且克服了 nullchecks。
    【解决方案6】:

    你必须做一个

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

    【讨论】:

      【解决方案7】:

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

      这是来自their documentation的示例:

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

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

      【讨论】:

        【解决方案8】:

        您也可以使用String.valueOf((Object) nullableString) 喜欢

        switch (String.valueOf((Object) nullableString)) {
        case "someCase"
            //...
            break;
        ...
        case "null": // or default:
            //...
                break;
        }
        

        查看有趣的 SO Q/A:Why does String.valueOf(null) throw a NullPointerException

        【讨论】:

          【解决方案9】:
          switch (String.valueOf(value)){
              case "null":
              default: 
          }
          

          【讨论】:

            【解决方案10】:

            基于@tetsuo 的回答,使用 java 8 :

            Integer i = ...
            
            switch (Optional.ofNullable(i).orElse(DEFAULT_VALUE)) {
                case DEFAULT_VALUE:
                    doDefault();
                    break;    
            }
            

            【讨论】:

              【解决方案11】:

              你不能。您可以在 switch 中使用原语(int、char、short、byte)和字符串(仅限 java 7 中的字符串)。基元不能为空。
              切换前单独检查i

              【讨论】:

              • 你也可以使用枚举。
              • 如果枚举为空,你也会遇到同样的问题。顺便说一句,switch 不能处理 null 很奇怪,因为它有一个默认子句
              • @LeonardoKenji 默认子句与 null 没有任何关系;无论您打开什么,它都会被取消引用以检查任何其他情况,因此默认子句不会处理 null 情况(在有机会之前抛出 NullPointerException)。
              • 我认为他的意思是默认子句应该将 null 处理为前一个案例未捕获的任何其他可能的枚举值
              【解决方案12】:

              我今天刚刚了解到,您不必仅仅因为 if 正在检查 null 而添加另一层缩进/大括号。您可以执行以下任一操作:

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

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

              【讨论】:

              • 这是有效的,因为 switch 本身就是一个块 - 你在这里有效地制作了一个单行 if 块,并且可以很容易地拥有if (i == null) return defaultValue; switch (i) { }
              【解决方案13】:

              只要考虑一下 SWITCH 的工作原理,

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2018-07-06
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2023-02-25
                • 2018-05-11
                相关资源
                最近更新 更多