当尝试创建一个标志枚举以使其包含多个状态时。我注意到一些奇怪的事情。当改变枚举的内部值时,它的名称不会改变。奇怪的是,这个内部值(在我的例子中是一个int)一直保持最大值,直到我显式地将值设置为0。
这是我用来进一步测试
的enumpublic enum flags {
None(0), flag1(1 << 0);
private int value;
private flags(int value) {
this.value = value;
}
public boolean hasFlag(flags flag) {
return (flag.value & value) == flag.value;
}
public void addFlag(flags flag) {
value |= flag.value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
我已经尝试了这些事情设置枚举值为0
flags flag = flags.None;
System.out.println(flag + " " + flag.getValue());
// prints: None 0
flag.addFlag(flags.flag1);
System.out.println(flag + " " + flag.getValue());
// prints: None 1
flag = flags.None;
System.out.println(flag + " " + flag.getValue());
// prints: None 1 even though
// its been set to flags.None which has a value of 0
flag = null;
flag = flags.None;
System.out.println(flag + " " + flag.getValue());
// prints: None 1. Even though the enum has been set to null before
// setting the value to null
flag = null;
flag = flags.flag1;
flag = flags.None;
System.out.println(flag + " " + flag.getValue());
// prints: None 1. Even though it is set to
// another value after being set to null
// and then set to None
flag.setValue(0);
System.out.println(flag + " " + flag.getValue());
// prints: None 0. As expected
我的问题如下:为什么这个值如此持久,这里发生了什么?
我认为您严重误解了Java枚举的目的和功能。根据定义,枚举是单例的,这意味着枚举的每个定义在每个运行时只构造一次。换句话说,你不能有两个flags.None
实例;当您的应用程序运行时,flags.None
永远是一个单一的东西。
你可以——然而——声明多个引用枚举的字段。就像您可能会说int i = 0
一样,您可以声明像flags myFlag = flags.None
这样的"标志"变量。这不会创建一个新版本的"None",但它是对"None"枚举的引用。修改"None"的状态(这是一个坏主意)将在整个应用程序中全局可见。
同样,您不应该在编写的枚举中加入您自己的位操作。我可以建议你看一下EnumSet吗?这将产生更具可读性的代码,并且是实现您想要完成的目标的有利方式。
问题是,枚举对于每个值只有一个实例(在您的示例中是None
和flag1
)。因此,编辑这个实例将永远改变它。如果你给None
添加了一个标志,那么原来的None
实例就是change(这和flags.None
是一样的),并且会一直保持这种状态,直到你再次改变它。
当你声明一个enum时,你实际上是在声明一系列静态对象。通过更改静态对象的成员(addFlag调用),您已经修改了它,因此所有进一步的引用都将引用相同的值。
你误解的另一部分是,当你将flag赋值给null或其他值时,你并没有清除枚举。Java是通过引用传递的,所以你所做的就是改变标志变量所引用的内容。这就是为什么在代码末尾,当您将value设置为0时,enum的None成员再次按预期显示0。您可以通过重新分配None和flag1枚举成员的值来解决这个问题。
最后,这并不是枚举的真正用途。我不确定你想用这个做什么,但你可以很容易地使用一个普通的类,基于你在这里提供的。当你需要严格的类型检查和明确的指示(命名)每个值的含义时,最好使用enum;例如,错误码枚举。但是其中的值不应该被重新赋值或修改。当然你可以这么做,我只是说这可能会让其他人看到你的作品感到困惑。