【问题标题】:Why does Java allow null value to be assigned to an Enum?为什么Java允许将空值分配给枚举?
【发布时间】:2014-04-14 09:17:16
【问题描述】:

这更像是一个设计问题,所以没有代码。如果您希望我这样做,我可以发布创建 Enum 并将其分配给 null 的代码。 :))

我最近一直在思考这个问题,但找不到一个好的理由。 Enum 常量隐含地是 static 和 final。枚举的意思是说-“我可以取一个存在于我身上的常数的值”。为什么允许 Enum 具有 null 值?为什么不将 Enum 的值隐式默认为 Enum.DEFAULT 或 Enum.None?这不是比允许 Enum 为 null 更好的方法吗?

【问题讨论】:

    标签: java enums


    【解决方案1】:

    首先null 表示不存在实例。提供像DEFAULT 或NONE 这样的默认常量将改变这个含义。其次,为什么你需要一些默认值来表示似乎不存在的东西?这就是null 的目的。基本上,您必须初始化并存储一个额外的对象,而该对象根本不应该存在。

    顺便说一句,这不是语言选择。如何实现枚举完全取决于您。您可以在枚举中提供另一个常量,例如 DEFAULT 或 UNKNOWN,并避免将 null 分配给代码中的引用。这就是众所周知的Null Object Pattern。但是说null赋值本身应该是编译器错误,那么我会说,因为Enum无论如何都会编译为Class,所以使用null表示不存在是完全有效的一个实例。

    允许null 的一个缺陷是在switch-case 中使用enum。以下代码将抛出 NPE,即使是 default 情况:

    public class Demo {
        enum Color {
            WHITE, BLACK;
        }
    
        public static void main(String[] args) {
            Color color = null;
    
            switch (color) {    // NPE here
            case WHITE: break;
            case BLACK: break;
            default: break;     // null value does not fall into the default
            }
        }
    }
    

    Java 也不允许 case null:,产生以下编译错误:

    枚举 switch case 标签必须是枚举常量的非限定名称

    【讨论】:

    • 相反,当 Enum 应该在自身内部取值时,将 Enum 指向 null 有什么意义? Oracle doc - 枚举类型是一种特殊的数据类型,它使变量能够成为一组预定义的常量。该变量必须等于为其预定义的值之一。
    • 我认为您只需要验证您的数据,因为例如当数据来自系统外部时,您无法保证发送请求的人知道使用默认值的后果是什么.
    • @Andrei 它不是默认值,而是一个值表示的空值。就像空集合一样。枚举中使用的空对象模式。
    • @AndreiI - 没错。如果一个人给我发送 null 而不是预期的 Enum 常量,它应该抛出一个运行时异常说 - “你应该发送一个 Enum 常量”。通过允许为空,它破坏了“我知道我能得到什么值”的设计。 (当然我可以将 calue 与 null 进行比较。但我不喜欢这样做)
    • @rohit - 我仍然认为 Enum 不应该分配给 null。 :)。我认为颜色颜色 = null; // 应该给出编译时错误(或运行时,如果我动态得到它。)
    【解决方案2】:

    Java 允许任何引用为空,并且对枚举的引用并没有那么特殊,以至于需要进行特殊情况来防止它,或者提供已经明确指定和充分理解的行为的另一个版本。 Null 已经是一个完全足够的哨兵值:我们不需要另一个。

    您的建议都不令人信服,因为它们都需要添加更多功能来支持它们。

    无论如何,要开始辩论这一点已经为时多年了。

    【讨论】:

    • 我的建议可能不正确。然而,它永远不会太晚.. 示例 - 基于字符串切换:P
    • 当然为时已晚。没有人会引入不向后兼容的语言更改。
    • 嗯,枚举足够特别,它们有自己的关键字,所以这是允许编译器将它们特殊处理并在将一个设置为 null 时拒绝的又一步(我同意 OP)。对我来说,省略似乎是一个任意的设计选择。这是否会成为语言是正交的。
    • @scorpiodawg 选择保持向后兼容性几乎不是“任意的”。
    【解决方案3】:

    我认为,Enum.DEFAULT 意味着您的枚举变量包含一个值,但 null 没有。如果是null,则可以解释为没有值,例如,您不能对其调用非静态方法。

    【讨论】:

    • 相反,当 Enum 应该在自身内部取值时,将 Enum 指向 null 有什么意义? Oracle doc - 枚举类型是一种特殊的数据类型,它使变量能够成为一组预定义的常量。该变量必须等于为其预定义的值之一。
    • @WhoAmI,因为枚举变量是对对象的引用,它可以指向任何内容(=空变量)或枚举值之一。
    【解决方案4】:

    您可以向枚举添加行为(域驱动设计),例如添加一个名为 prettyPrint() 的公共方法,该方法根据您的枚举值返回一个字符串。默认/空值如何提供此自定义方法的实现?

    如果您想用枚举表示 null,那么您必须通过手动使用带有 NONE 值的 null 对象模式和基于 NONE 值的 prettyPrint() 自定义实现来明确这一点。

    【讨论】:

      【解决方案5】:

      老了,我知道。我是半Java。

      为什么允许 Enum 有一个空值?

      我相信很少有用例。例如,我们需要初始化一个枚举变量来告诉我们它实际上不是枚举的一部分,但我们可以稍后更改它的值,使其成为枚举的一部分。如果无法将枚举变量初始化为null,那么我们将污染枚举定义。因此,在我看来,LibEnum.NONE 之类的字段会使库变得混乱。例如,您不希望在其他方法之间使用LibEnum.NONE 作为方法的参数。

      我发现它在我的词法扫描器中非常有用,包括关键字和标点符号。假设您有一个标识符"blah"。然后你想检查它是否是任何关键字。

      private Keywordv getKeywordValue(String id)
      {
          switch (id.length())
          {
              case 2:
              {
                  switch (id)
                  {
                      case "do": return Keywordv.DO;
                      case "if": return Keywordv.IF;
                      case "is": return Keywordv.IS;
      
                      // ...
                  }
      
                  break;
              }
      
              // ...
          }
      
          return null;
      }
      

      感谢null,可以轻松验证标识符是否不是关键字。

      id == null
      

      否则我们必须定义类似Keywordv.__NULL

      【讨论】:

        猜你喜欢
        • 2017-04-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-20
        • 2012-03-01
        • 2021-10-21
        • 2014-07-08
        • 1970-01-01
        • 2011-02-27
        相关资源
        最近更新 更多