是否应该为 Java 枚举定义 null/未知值?
当您为接口中可能“未定义”的内容定义枚举时,是否应该
- 为其定义一个单独的枚举值,或者
- 在这些情况下仅使用 enumValue = null ?
例如,
serviceX.setPrice(PricepriceEnum)
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),但感觉更干净,更少误导(?)
When you define an enum for something that can be "undefined" in your interfaces, should you
- define a separate enum value for that, or
- just use enumValue = null for those situations?
For example,
serviceX.setPrice(Price priceEnum)
enum Price {
CHEAP, EXPENSIVE, VERRRY_EXPENSIVE, UNKNOWN
}
and priceEnum.UNKNOWN when needed
or
enum Price {
CHEAP, EXPENSIVE, VERRRY_EXPENSIVE
}
and priceEnum = null when needed?
Having a little debate on this. Some points that come to mind:
- using Price.UNKNOWN saves some "if (price == null)" code. You can handle Price x's all values in a single switch-case
- Depending on view technology, it may be easier to localize Price.UNKNOWN
- using Price.UNKNOWN kind of causes "magic number" problem in the code, IMO. Here we have Price.UNKNOWN, elsewhere maybe Color.UNDEFINED, Height.NULLVALUE, etc
- using priceValue = null is more uniform with how other data types are handled in Java. We have Integer i = null, DomainObject x = null, String s = null for unknown values as well, don't we?
- Price.UNKNOWN forces you to decide whether null value is allowed univerally for all use cases. We may have method Price getPrice() which may return Price.UNKNOWN and setPrice(Price p) which is not allowed to accept Price.UNKNOWN. Since Price.UNKNOWN is always included in the enum's values, those interfaces look a little unclean. I know priceValue = null has the same problem (you cannot define in the interface whether null is accepted or not) but it feels a little cleaner and a little less misleading(?)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
这实际上是应用空对象模式的示例。恕我直言,拥有一个虚拟对象总是比 null 更好。例如,您可以向空对象添加虚拟方法,而不是将代码分散在各处进行空检查。非常方便。
此外,
enum
的名称还为您提供了一些额外的语义:价格是否未知、未定义、不值得信赖、还不知道?如果价格为null,这意味着什么?更新:正如 Aaron Digulla 指出的那样,空对象模式需要内存。但大多数时候实际情况并非如此。在传统的实现中,您通常在任何地方都使用 Null 对象的单例,因为不需要单独的实例。使用枚举会变得更好,因为你可以免费获得单例语义。
另一点是,
null
引用和对某个对象的引用占用相同的内存量(比如在 32 位机器上为 4 个字节)。正是被引用的对象占用了一些额外的内存。但如果这是一个单例,那么这里实际上没有内存开销。This is actually an example of applying Null Object pattern. IMHO it is always better to have a dummy object rather than null. For instance you can add dummy methods to null-object rather than scattering your code with null-checks all over the place. Very convenient.
Also the name of the
enum
gives you some additional semantics: is the price unknown, undefined, not trustworthy, not yet known? And what does it mean if the price is null?UPDATE: As Aaron Digulla points out, Null Object pattern requires memory. But this isn't actually the case most of the time. In the traditional implementation you typically have a singleton for Null object used everywhere as there is no need for separate instances. It gets even better with enums because you get singleton semantics for free.
Another point is that
null
reference and reference to some object occupy the same amount of memory (say 4 bytes on 32-bit machine). It is the object being referenced that occupies some extra memory. But if this is a singleton, there is practically no memory overhead here.如果这是一个有效的价格值,我会说选择
Price.UNKNOWN
。我同意您提到的处理空引用的缺点,并且我认为它们足以促使我们做出决定。
新语言,以 Scala 为例(以及一些较旧的语言,Haskell),一起远离空引用并使用选项/也许 monads 代替......有充分的理由。
I'd say go with
Price.UNKNOWN
if that's a valid value for a price.I agree with the drawbacks of dealing with null references that you mention, and I think they motivate the decision enough.
New languages, take Scala for example (and some older ones, Haskell) strain away from null references all together and uses option / maybe monads instead... for good reasons.
这取决于您将如何使用这个枚举。如果你在 switch/case 语句中使用它,那没关系。
如果您在枚举中创建方法,您实际上必须定义 UNKNOWN。
例如你可以定义抽象方法
public abstract Icon icon();
到您的枚举中,然后为 Price 的每个成员实现此方法。也许您会想要显示未知价格的问号。在这种情况下,只需实现创建适当图标的方法
icon()
即可。It depends how are you going to use this enum. If you use it in switch/case statements it does not matter.
If you create method(s) into the enum you actually must define UNKNOWN.
For example you can define abstract method
public abstract Icon icon();
into your enum and then implement this method for each member of Price. Probably you will want to display question mark for unknown price. In this case just implement method
icon()
that creates appropriate icon.有Enum-Switch-Null-Trap。
所以看起来就像任何作为对象的属性一样,
如果不存在则为
null
。There is the Enum-Switch-Null-Trap.
So it seems that just like with any property that is an object,
if it doesn't exist then it is
null
.颜色或高度将在程序逻辑中使用。它们无法处理未定义的颜色。
价格是用户数据,可能是未知的。
颜色可能是其他用户数据,但要在代码中用作颜色,必须定义它们。
所以价格可能是未知的(而不是空),颜色不是(空可能表示错误)。
Color or Height would be used in the program logic. Them cannot handle with an undefined color.
A Price is userdata and may be unknown.
The color may be userdata else, but to be used as color in the code, they must be defined.
So Price may be UNKNOWN (instead of null), Color not (null may indicate error).