【问题标题】:Using enums to implement utility classes and singletons使用枚举实现实用程序类和单例
【发布时间】:2015-01-02 19:29:45
【问题描述】:

Peter Lawrey 在他的博客上写到 Two Uses of Enums that most people forget

首先,我没有忘记——我什至没有意识到 :)

这些方法既简洁又美观 - 与实现相同目标的更传统方法(例如使用 final 类和 private 实用程序类的构造函数)相比,除了简洁之外还有其他好处吗?

另外,是否有任何问题(除了让没有预料到的程序员感到困惑)?

【问题讨论】:

  • 对于反对者,我很想知道为什么这个问题被反对了。这不是关于如何做事“错误的方式”的第一个问题,同时,事实证明,Java 的“圣经”(Effective Java)实际上至少在单例的情况下推荐这种方法。

标签: java enums


【解决方案1】:

对我来说,将enums 用于真正的 枚举似乎更直观。

【讨论】:

  • 没有理由不这样做,并在其他有益的情况下使用它们。
  • @ColinD - 我只是不认为它是直观的。当我看到一个枚举时,我会期待一组颜色、状态等。
  • @Brian:它可能并不完全直观,但枚举单例模式至少是任何阅读 Effective Java 的人都应该知道和认识的(实际上,每个人都应该读那个)。不过,正如我在回答中提到的那样,我不喜欢链接帖子中的第一次使用,而且我认为将枚举单例设为类不向用户公开的内部细节通常是个好主意。
  • 您写道:“它可能并不完全直观”,这是真的。您可以使用一些不需要枚举的简单直观的最佳实践。那我为什么要误用一个呢?
  • @ColinD aaaah,所以它在更新 Effective Java - 我还没读过!
【解决方案2】:

我不太同意第一次使用该帖子中的enum。如果您想要一个不可实例化的实用程序类,只需给它一个私有构造函数。就这么简单,在我看到的这种情况下,enum 并没有提供额外的好处。

在实用程序类中对单例使用枚举非常棒,但我通常会尽量保留使用 enum 的事实作为内部实现细节。例如,参见 Guava 的 Predicates 类,它使用 enum 来强制执行某些 Predicates 的单个实例,例如 alwaysTrue()。但它不会向用户公开enum

至于其他好处:是的,还有其他好处,例如内置的可序列化性和每个类加载器绝对强制执行 enum 常量的单个实例,即使在反序列化时也是如此。

【讨论】:

  • 我相信人们会问为什么我接受了这篇文章而不是其他人 - 这实际上是因为@ColinD 对这个问题的另一个答案的评论 - 解释了枚举单例模式被提出在 Effective Java 中 - 这让我想给 @ColinD 点 :)
【解决方案3】:

在这两种情况下,我的第一直觉是它们都是令人讨厌的黑客。

然而,在单例的情况下,我认为您可以合理地争辩说,在枚举常量是一等对象的语言中,单例和只有一个值的枚举在概念上没有区别。我仍然不确定我是否真的会使用该技术,特别是因为单例模式由于其他原因非常不受欢迎。

将枚举用于实用程序类更难防御。将一组实用方法表示为枚举没有概念上的理由(可以说没有理由将它们表示为一个类,但这是另一回事)。似乎以这种方式实现实用程序方法的唯一理由是通过忽略已建立的约定来节省一点打字。

【讨论】:

    【解决方案4】:

    除了将 Enums 用于非枚举的东西会引起混淆之外,将 Enums 用于单例与其他硬编码单例范围的方法一样具有相同的缺点,基本上它会阻碍测试并使继承或其他扩展变得困难。让单例范围像 Spring 一样由工厂控制是一个更好的主意。

    【讨论】:

    • 对于服务等当然是这样,但是对于其他类型的单例,使用enum 可能是个好主意。
    【解决方案5】:

    我不喜欢这种方法。枚举应该保持简单的枚举。它们最多应该包含转换逻辑。将枚举用作单例或接口在我看来就像是一个混淆代码。

    【讨论】:

    • @Falcon:我不同意枚举需要“简单”,或者它们最多应该包含转换逻辑。枚举很好,例如,可以为某个对象建模一组可能的状态,让每个状态常量都有自己的一组特定于状态的抽象方法的实现是很有意义的。
    • 我宁愿在这种情况下使用状态模式。这显然是对枚举的滥用,而枚举只是为了枚举。没有任何好处,只是值得混淆。
    • @Falcon:在你看来,这是一个“明显的误用”。出于某种原因,Java 制作了完整的枚举类而不是美化的字符串常量。 Josh Bloch 建议在 Effective Java 中使用枚举单例,并列举了它们相对于实现单例的传统方式的众多优势。不管你喜不喜欢,Java 中的枚举都是一种用于创建具有一组固定实例的类的构造……而不仅仅是简单的无行为枚举。
    • @Falcon:“某本书”中的“某人”……好一个!您似乎只是对枚举必须是什么有一些固定的想法,并且不愿意接受 Java 枚举比您认为它们应该具有的强大得多的事实。与枚举常量相关的代码可能会让您“混淆”,但我认识的大多数 Java 开发人员都很好理解。
    • @Falcon:如果存在与枚举常量相关联的行为,最好将可维护性和干净代码直接与常量相关联,而不是在枚举上执行 switch 或类似的操作在您的代码中的其他地方。在我看来。无论如何,这显然有点太激烈了,所以……只好同意不同意这里。