【问题标题】:Can I add and remove elements of enumeration at runtime in Java我可以在 Java 运行时添加和删除枚举元素吗
【发布时间】:2010-10-03 11:11:03
【问题描述】:

可以在运行时在 Java 中的枚举中添加和删除元素吗?

例如,我可以从文件中读取枚举的标签和构造函数参数吗?


@saua,这只是一个是否真的可以出于兴趣而做的问题。我希望有一些巧妙的方法来改变正在运行的字节码,也许使用BCEL 或其他东西。我还跟进了this question,因为我意识到我并不完全确定何时应该使用枚举。

如果我希望能够在运行时安全地更改内容,我非常相信正确的答案是使用确保唯一性的集合而不是枚举。

【问题讨论】:

  • 通常,如果你想要这个,你就是在滥用它们。你能告诉我们你需要它做什么吗?
  • 我的答案是在后台用HashMap实现的,所以可以保证唯一性。
  • FWIW,它有助于测试打开枚举的默认情况。

标签: java enums


【解决方案1】:

不,枚举应该是一个完整的静态枚举。

在编译时,您可能希望从另一个源文件生成枚举 .java 文件。你甚至可以像这样创建一个 .class 文件。

在某些情况下,您可能需要一组标准值,但允许扩展。执行此操作的常用方法是为接口设置一个interface,并为标准值设置一个实现interface 的enum。当然,当您只有 interface 的引用时,您将无法使用 switch。

【讨论】:

  • 您知道我在哪里可以找到实现此类接口的枚举的好例子吗?我会对这样的功能感兴趣。
  • @megalucio 我想我在想java.nio.file.StandardOpenOption docs.oracle.com/javase/8/docs/api/java/nio/file/… 但也有java.net.StandardProtocolFamily 和javax.tools.StandardLocation。
  • 非常感谢。我会调查一下。
【解决方案2】:

在幕后,枚举是具有私有构造函数和一堆枚举类型的公共静态最终值的 POJO(参见here 示例)。事实上,在 Java5 之前,以这种方式构建自己的枚举被认为是最佳实践,并且 Java5 引入了 enum 关键字作为简写。请参阅Enum<T> 的来源以了解更多信息。

因此,使用由构造函数读取或传递给它的公共静态最终常量数组编写自己的“TypeSafeEnum”应该没有问题。

另外,帮自己一个忙,覆盖 equals、hashCode 和 toString,如果可能的话,创建一个 values 方法

问题是如何使用这样的动态枚举...您无法从文件中读取值“PI=3.14”来创建enum MathConstants,然后继续使用MathConstants.PI,无论您想要什么...... .

【讨论】:

  • 永远不要写一个公共的静态非空数组字段——它们不是常量。
  • 这是真的吗?由于引用末尾的实例是不可变的,我不确定您如何实际更改它......它是否只是跨越了它是否是数组实例?
  • 集合和数组都是可变对象(和“对象”)。拥有“最终”数组只是意味着您不会调整数组的大小或以其他方式重新分配数组,但它仍然可以让您替换已经适合其边界的值。
【解决方案3】:

我需要做这样的事情(出于单元测试的目的),我遇到了这个 - EnumBuster: http://www.javaspecialists.eu/archive/Issue161.html

它允许添加、删除和恢复枚举值。

编辑:我才刚刚开始使用它,发现 java 1.5 需要一些细微的变化,我目前坚持这样做:

  • 添加数组 copyOf 静态辅助方法(例如采用这些 1.6 版本:http://www.docjar.com/html/api/java/util/Arrays.java.html)
  • 将 EnumBuster.undoStack 更改为 Stack&lt;Memento&gt;
    • 在 undo() 中,将 undoStack.poll() 更改为 undoStack.isEmpty() ? null : undoStack.pop();
  • 对于我目前尝试过的 java 1.5 枚举,字符串 VALUES_FIELD 必须是“ENUM$VALUES”

【讨论】:

【解决方案4】:

我在formative project of my young career 上遇到了这个问题。

我采用的方法是在外部保存枚举的值和名称,最终目标是能够编写看起来尽可能接近语言枚举的代码。

我希望我的解决方案如下所示:

enum HatType
{
    BASEBALL,
    BRIMLESS,
    INDIANA_JONES
}

HatType mine = HatType.BASEBALL;

// prints "BASEBALL"
System.out.println(mine.toString());

// prints true
System.out.println(mine.equals(HatType.BASEBALL));

我最终得到了这样的结果:

// in a file somewhere:
// 1 --> BASEBALL
// 2 --> BRIMLESS
// 3 --> INDIANA_JONES

HatDynamicEnum hats = HatEnumRepository.retrieve();

HatEnumValue mine = hats.valueOf("BASEBALL");

// prints "BASEBALL"
System.out.println(mine.toString());

// prints true
System.out.println(mine.equals(hats.valueOf("BASEBALL"));

由于我的要求是必须可以在运行时将成员添加到枚举中,因此我也实现了该功能:

hats.addEnum("BATTING_PRACTICE");

HatEnumRepository.storeEnum(hats);

hats = HatEnumRepository.retrieve();

HatEnumValue justArrived = hats.valueOf("BATTING_PRACTICE");
// file now reads:
// 1 --> BASEBALL
// 2 --> BRIMLESS
// 3 --> INDIANA_JONES
// 4 --> BATTING_PRACTICE

我将其称为动态枚举“模式”,您阅读了有关 the original design 和 its revised edition 的信息。

两者的区别在于修改版是在我真正开始摸索OO和DDD之后设计的。我在写名义上的程序 DDD 时设计的第一个,时间压力不小。

【讨论】:

  • 您帖子中的链接现在受作者保护,无法查看
  • 这不会破坏 Java 的最佳实践,它假定枚举器是静态的和最终的?这几乎就像您将本地文件用作常量值的数据库,必须重新读取这些常量值以保证最新,因为之前读取的数据可能是陈旧的。
【解决方案5】:

您可以在运行时从源代码加载 Java 类。 (使用 JCI、BeanShell 或 JavaCompiler)

这将允许您根据需要更改 Enum 值。

注意:这不会更改引用这些枚举的任何类,因此这在现实中可能不是很有用。

【讨论】:

    【解决方案6】:

    一个广泛使用的工作示例是在修改后的 Minecraft 中。请参阅 Github 上的 EnumHelper.addEnum() 方法

    但是,请注意,在极少数情况下实践经验表明,添加 Enum 成员可能会导致 JVM 优化器出现一些问题。确切的问题可能因不同的 JVM 而异。但从广义上讲,优化器似乎会假设 Enum 的某些内部字段,特别是 Enum 的 .values() 数组的大小不会改变。见issue discussion。推荐的解决方案是不要让.values() 成为优化器的热点。因此,如果在运行时添加 Enum 的成员,则应仅在应用程序初始化时执行一次,然后缓存 .values() 的结果以避免使其成为热点。

    优化器的工作方式和它检测热点的方式是模糊的,并且可能在不同的 JVM 和不同的 JVM 版本之间有所不同。如果您不想在生产代码中冒此类问题的风险,请不要在运行时更改 Enum。

    【讨论】:

      【解决方案7】:

      您可以尝试将属性分配给您尝试创建的 ENUM,并使用加载的属性文件静态构造它。大黑客,但它的工作原理:)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-11-27
        • 2021-05-17
        • 2011-01-28
        • 2022-12-04
        • 1970-01-01
        • 2015-11-29
        • 1970-01-01
        相关资源
        最近更新 更多