你应该为Java枚举定义一个null / unknown值吗?

当你定义一个枚举的东西,可以在你的界面“未定义”,如果你

  • 定义一个单独的枚举值,或
  • 只是使用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)”代码。 您可以在一个开关柜中处理价格x的所有值
  • 根据视图技术,可能更容易本地化Price.UNKNOWN
  • 在代码中使用Price.UNKNOWNtypes的原因“幻数”问题,IMO。 这里我们有Price.UNKNOWN,其他地方可能是Color.UNDEFINED,Height.NULLVALUE等等
  • 使用priceValue = null与Java中处理其他数据types的方式更加一致。 对于未知值,我们有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),但感觉有点干净,有点误导(?)

这实际上是一个应用Null对象模式的例子。 恕我直言,总是有一个虚拟的对象,而不是null。 例如,您可以将虚拟方法添加到空对象,而不是使用空值检查来散布您的代码。 很方便。

此外, enum的名称给你一些额外的语义:价格是未知的未定义的不可信的 ,还不知道的 ? 如果价格为零 ,这意味着什么?

更新:正如Aaron Digulla指出的那样,Null Object模式需要内存。 但大多数情况下并非如此。 在传统的实现中,你通常有一个用于Null对象的单例,因为不需要单独的实例。 使用枚举会更好,因为你可以免费获得单例语义。

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

如果这是一个有价值的价格,我会说与Price.UNKNOWN去。

我同意你提到的处理空引用的缺点,我认为他们足以激励这个决定。

新的语言,以Scala为例(和一些较老的Haskell)一起消除空引用,并使用选项/也许monad代替…有充分的理由。

这取决于你将如何使用这个枚举。 如果你在switch / case语句中使用它并不重要。 如果你在枚举中创build方法,你实际上必须定义UNKNOWN。

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

放到你的枚举中,然后为Price的每个成员实现这个方法。 可能你会想显示问号未知的价格。 在这种情况下,只需实现方法icon() ,创build适当的图标。

有Enum-Switch-Null-Trap 。 因此,就像任何属性是一个对象,如果它不存在,那么它是null

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

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