【问题标题】:Java enums mutability usecases and possibilities?Java 枚举可变性用例和可能性?
【发布时间】:2012-10-22 16:40:59
【问题描述】:

我不知道我是否是唯一一个知道这一点的人,但枚举的值并不是隐含的最终值,可以修改。

  enum EnumTest {
    TOTO("TOTO 1"),
    TATA("TATA 2"),
    ;

    private String str;

    private EnumTest(String str) {
      this.str = str;
    }

    @Override
    public String toString() {
      return str;
    }
  }

  public static void main(String[] args) {
    System.out.println(EnumTest.TATA);
    EnumTest.TATA.str = "newVal";
    System.out.println(EnumTest.TATA);
  }

TATA 2
newVal

这些值通常在实例创建时初始化 (TOTO("TOTO 1")),但是,除了我自己,我从未见过任何人使用 final 关键字来表示应该是不可变的枚举变量。这不是问题的重点,只是想知道我是否是唯一一个知道这一点的人。


我想知道是否有任何用例可以创建可变枚举?

而且我还想知道我们可以用枚举做些什么的限制(不管是不是好的做法)。 我还没有测试过,但也许可以用 Spring bean 注入枚举? 至少看起来我们可以注释每个实例(例如@Deprecated 可以正常工作)以及方法。

【问题讨论】:

  • 对字段使用final 是枚举中的常见做法。

标签: java spring enums


【解决方案1】:

一个可能的用例是延迟初始化(在第一次使用时计算某些字段值,如果它们经常根本不使用),或者是“正常”可变单例对象(如注册表等)。

不过,在大多数情况下,枚举对象应该是不可变的,并且它们的字段是最终的。

【讨论】:

    【解决方案2】:

    虽然您在这里指出了一个有趣的事实,但就我而言,可变枚举字段没有用例。使用这种语言的“功能”(“错误”?)是一个坏主意的原因有很多,其中最重要的原因是可能会使其他开发人员感到困惑。

    【讨论】:

    • 我同意枚举通常被认为是不可变的,因此创建可变枚举可能会导致误用。我建议与枚举相关的可变值最好在EnumMap 中提供帮助。也就是说,我认为说“没有用例”太强了。在实践中,枚举代表了单例模式(固定数量的实例)的扩展,并且考虑到这一点,可以像任何其他类一样使用。我再次重申,大多数开发人员习惯于将它们视为 IMMUTABLE,因此可变枚举可能会导致维护问题。
    • 感谢上帝,因为它们被认为是不可变的,通常没有 setter :)
    • @JohnB,如果您能想到一个很好的例子,说明使用带有可变字段的枚举可以说比任何替代方法都好,我希望将其视为一个单独的答案。但我的回答仍然是:就我而言,没有用例,至少没有一个用普通的class 无法更好地服务的用例。您对“实践中”的枚举是正确的,但从概念上讲,它们更接近 boolean(具有固定的 2 个可能值)或 int(固定的 2^32-1 个可能值) .你能想象在true 上有可变字段吗?
    【解决方案3】:

    作为对 Alex D 的回答和评论的回应,我接受了他发布一个可能的用例的建议。让我们以可以计算重力等的行星的旧标准枚举示例为例。想象一下,你想保持每个星球上人类殖民地的数量。是的,您可以使用 EnumMap,但我可以看到可能需要越来越多的可变字段的情况,并且为每个值使用单独的映射或单独的类来保存与枚举关联的可变值将是违反直觉的。

    正如我在 cmets 中所说,一般来说,我认为枚举通常是并且应该是不可变的,但我觉得说没有用例太强了。

    【讨论】:

      【解决方案4】:

      我认为没有理由禁止在enums 中使用可变字段。一个人使用enum 来声明该类型的值属于有限(和已知)的一组可能性中的一个。这并不意味着这些值不能具有随时间演变的属性。

      有些人要求提供此类场景的用例。一个示例是使用enum 类型,例如ErrorCategory,它将错误分类为任意数量的预定义类别之一(例如:DocumentationErrorSemanticErrorLayoutError...)。

      假设那些ErrorCategories 具有requiresInstantInterventionshouldFailBuild 等属性。我可以想象这些属性的值可能会随着时间而改变,或者它们可以是用户可配置的。

      我意识到有(很多)其他方法可以实现这一点(例如确实是上面提到的EnumMaps),人们总是可以争论风格,但对我来说,enums 的这种使用在本身。由于enums 是java 中的引用类型,== 的行为与一开始就没有可变属性一样。

      【讨论】:

        【解决方案5】:

        枚举实例是枚举类的公共静态最终字段。所以如果它是可变的,你可以在另一个线程中修改它的状态。这可能不是您想要的,如果您没有意识到这一点,可能会导致问题。

        【讨论】:

          【解决方案6】:

          如果我错了,请纠正我,但 Enum 是 Java 中最简单(更重要的是,也许是最安全)的方式,可以在应用程序的整个生命周期中实际拥有一个真正的单例(如果我们不能使用 CDI),因为使用如果过于天真地实现,标准的静态 getInstance() 方法模式仍然可能产生同一个类的多个实例。

          [同样地,实现 Serializable 也可能导致产生不止一个实例,而 Enum 已经为我们处理好了它,没有意外的结果和样板。]

          此外,文档清楚地表明 Enum 类型不仅仅是常量。所以我想说,只要绝对有必要在应用程序中拥有一个真正的单例,一个 Enum(甚至是可变的)可能被认为是合理使用。

          但是,在我看来,这个问题不可避免地倾向于有点固执己见,并且不能完全公正地回答,因为有些开发人员支持不变性(我并不是说没有充分的理由)但在这方面不那么严格的开发人员和基本文档技能有时可能与代码本身一样重要(是什么阻止我们强调枚举常量的内部状态实际上在 javadoc 中是可变的这一事实?)。

          【讨论】:

          • 我觉得这个大段很难理解;这是一个答案,还是一个新问题?问题不应作为答案发布; Stack Overflow is not a forum。如果它一个答案,你能把它分成几段以便于阅读吗?
          • 这是一个答案。明天我会打破它,是的,它很长,对此感到抱歉。
          【解决方案7】:

          我使用可变枚举来处理不受我控制的序列化数值,我希望在使用和中继时保留这些数值。例如

          VALUE_A = 1
          VALUE_B = 13
          VALUE_C = 17
          VALUE_UNRECOGNIZED
          

          例如如果我收到 61 的序列化值怎么办?我不认识它,所以我没有枚举常量,我不会以其他方式处理它,但我不想让它失败:我想保留序列化值并将其转发。

          也许规范的方法可能是创建一个具有两个字段的新类:一个不可变的枚举和原始值,但是,恕我直言,这似乎更麻烦。

          【讨论】:

            【解决方案8】:

            我想知道是否有任何用例可以创建可变枚举?

            枚举常量本身不是可变的,它们的字段是可变的。

            【讨论】:

            • 这就是他的意思,据我所知。
            • 是的,这就是我的意思,但“通用”用例是创建不可变枚举,但没有人使用“final”
            • 这是因为很少有 enum 带有 public 字段或更改字段恕我直言的公共方法。但我同意这些字段应声明为final
            • 听起来 OP 很了解这一点,并在询问为什么该语言旨在支持这一点,所以 DV 不回答问题。
            • @djechlin 享受!在我的工作中,准确比理解更重要。也许您使用的工具能够理解您的意图
            猜你喜欢
            • 2012-03-27
            • 2011-12-23
            • 1970-01-01
            • 2011-03-16
            • 1970-01-01
            • 2013-07-22
            • 2021-08-08
            • 1970-01-01
            • 2020-11-17
            相关资源
            最近更新 更多