【问题标题】:Should you define a null/unknown value for Java enums?您是否应该为 Java 枚举定义一个空值/未知值?
【发布时间】:2011-08-22 07:56:18
【问题描述】:

当您为接口中可能“未定义”的事物定义枚举时,您应该

  • 为此定义一个单独的枚举值,或者
  • 在这些情况下只使用 enumValue = null 吗?

例如,

serviceX.setPrice(Price priceEnum)

enum Price {
 CHEAP, EXPENSIVE, VERRRY_EXPENSIVE, UNKNOWN
}

和 priceEnum.UNKNOWN 在需要时

enum Price {
 CHEAP, EXPENSIVE, VERRRY_EXPENSIVE
}

需要时 priceEnum = null?

对此进行了一些辩论。想到的几点:

  • 使用 Price.UNKNOWN 可以节省一些“if (price == null)”代码。您可以在单个 switch-case 中处理 Price x 的所有值
  • 根据视图技术,本地化 Price.UNKNOWN 可能更容易
  • 使用 Price.UNKNOWN 会导致代码中出现“幻数”问题,IMO。这里有 Price.UNKNOWN,其他地方可能有 Color.UNDEFINED、Height.NULLVALUE 等
  • 使用 priceValue = null 与 Java 中其他数据类型的处理方式更加一致。对于未知值,我们也有 Integer i = null、DomainObject x = null、String s = null,不是吗?
  • Price.UNKNOWN 强制您决定是否对所有用例普遍允许空值。我们可能有方法 Price getPrice() 可能返回 Price.UNKNOWN 和 setPrice(Price p) 不允许接受 Price.UNKNOWN。由于 Price.UNKNOWN 总是包含在枚举值中,因此这些接口看起来有点不干净。我知道 priceValue = null 有同样的问题(你不能在界面中定义 null 是否被接受)但感觉更干净一点,误导性更小(?)

【问题讨论】:

  • 你不能在 switch 中使用null,这是一个主要的缺点。
  • 大家好。减轻空值处理的负担,以及更具体地了解“空值”的含义的能力是 Price.UNKNOWN 的优点。此外,关于“空对象模式”的好点,它使 Price.UNKNOWN 感觉像是更“有效”和普遍接受的解决方案。但是,没有人对 Price.UNKNOWN 的负面影响有同样的感受?我觉得 Price.UNKNOWN 污染了枚举的值集。在 showAllPrices() 中说,我不能再枚举所有枚举值,我必须添加 if (!Price.UNKNOWN),这感觉有点脏和“幻数”。
  • @user449236:如果您不喜欢检查这个特殊值,请为每个枚举添加一个 display 标志并改为检查它。更清洁、更灵活。
  • 看,这很简单,以 C 为例,枚举不能有 null(因为它们实际上是真正的 int)。在我的理解中,有一个未声明的值(即 null)是一种不好的做法,通常是一个错误。未声明的值不能说明目的是什么,即它应该被跳过,它是错误的结果(因此应该抛出异常)
  • 对此的(很晚)输入。根据我的经验 - 如果您正在编写要重用、扩展或构成 API 一部分的代码 - 您将不得不考虑客户端代码传递或无论如何设置 null 以保持健壮。这通常会减轻特殊 Foo.NIL 值带来的任何好处。这也使得编写与类型无关的方法/实用程序变得更加困难,因为它们(通常)不会知道其中一个值是“特殊的”。

标签: java enums null


【解决方案1】:

这其实是一个应用Null Object pattern的例子。恕我直言,拥有一个虚拟对象而不是空对象总是更好。例如,您可以向空对象添加虚拟方法,而不是将代码分散在各处进行空检查。很方便。

enum 的名称还为您提供了一些额外的语义:是价格未知未定义,不值得信赖,还没有已知?如果价格为 null 是什么意思?

更新:正如 Aaron Digulla 指出的,空对象模式需要内存。但实际上大多数时候情况并非如此。在传统的实现中,您通常会在任何地方使用 Null 对象的单例,因为不需要单独的实例。使用枚举会变得更好,因为您可以免费获得单例语义。

另一点是null 引用和对某个对象的引用占用相同数量的内存(比如 32 位机器上的 4 个字节)。正是被引用的对象占用了一些额外的内存。但如果这是一个单例,这里几乎没有内存开销。

【讨论】:

  • +1 用于提及模式。需要注意的一点:人们不使用该模式的主要原因是因为它需要内存(null 指针不需要内存)。但是他们忘记了这会使代码更复杂(-> 花费更多时间来开发和调试它),复杂性与错误的数量密切相关,更多的代码行意味着 代码 需要更多的内存,所以该决定通常是所有边界的损失。
  • 好点,我实际上编辑了我的问题以解决您的评论。
  • The main reason why people don't use the pattern is because it needs memory (null pointers don't need memory) 主要原因是性能,因为空检查很容易被 JIT 消除,空检查是硬件辅助等等。
【解决方案2】:

如果价格合理,我会说Price.UNKNOWN

我同意你提到的处理空引用的缺点,我认为它们足以激发决策。

新语言,以 Scala 为例(以及一些较旧的语言,Haskell)完全摆脱空引用并使用选项/也许是 monads... 有充分的理由。

【讨论】:

    【解决方案3】:

    这取决于你打算如何使用这个枚举。如果您在 switch/case 语句中使用它,则无关紧要。 如果您在枚举中创建方法,您实际上必须定义 UNKNOWN。

    例如你可以定义抽象方法 public abstract Icon icon();

    进入您的枚举,然后为 Price 的每个成员实现此方法。可能您会希望显示未知价格的问号。在这种情况下,只需实现创建适当图标的方法icon()

    【讨论】:

      【解决方案4】:

      Enum-Switch-Null-Trap。 所以看起来就像任何作为对象的属性一样, 如果不存在则为null

      【讨论】:

        【解决方案5】:

        颜色或高度将在程序逻辑中使用。他们无法处理未定义的颜色。 价格是用户数据,可能未知。 颜色可能是其他用户数据,但要在代码中用作颜色,必须定义它们。

        所以价格可能是未知的(而不是 null),颜色不是(null 可能表示错误)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-04-12
          • 2020-09-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-07-18
          相关资源
          最近更新 更多