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

时间:2009-01-25 22:08:54

标签: java enums

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

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


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

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

7 个答案:

答案 0 :(得分:40)

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

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

在某些情况下,您可能需要一组标准值但允许扩展。通常的方法是使用interface接口和enum为标准值实现interface。当然,当您只引用switch时,您将无法interface

答案 1 :(得分:13)

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

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

另外,请帮助自己并覆盖equalshashCodetoString,如果可能,请创建values方法

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

答案 2 :(得分:9)

我需要做这样的事情(用于单元测试),我遇到了这个 - 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更改为堆栈<Memento>
    • 在undo()中,将undoStack.poll()更改为undoStack.isEmpty()? null:undoStack.pop();
  • 我目前尝试过的java 1.5枚举字符串VALUES_FIELD需要为“ENUM $ VALUES”

答案 3 :(得分:6)

我在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 designits revised edition

两者之间的区别在于修订后的版本是在我真正开始研究OO和DDD之后设计的。第一个我设计的时候,我仍然在时间压力下写下名义上的程序性DDD。

答案 4 :(得分:0)

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

这样您就可以根据需要更改枚举值。

注意:这不会改变任何引用这些枚举的类,所以这在现实中可能不太有用。

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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

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

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