【发布时间】:2012-12-13 23:22:51
【问题描述】:
老方法,如果我们想在一些复杂的位掩码上switch,我们可以很容易地这样做(我头脑中的一个随机示例只是为了演示这个问题):
private static final int MAN = 0x00000001;
private static final int WOMAN = 0x00000002;
// ...alive, hungry, blind, etc.
private static final int DEAD = 0xFF000000;
public void doStuff(int human) {
switch (human) {
case MAN | DEAD:
// do something
break;
// more common cases
}
}
现在,由于我们使用enums 和EnumSets,我有时想做类似的事情:
enum Human {
MAN, WOMAN, DEAD; // etc.
}
public void doStuff(EnumSet human) {
switch (human) {
case Human.MAN | Human.DEAD:
// do something
break;
// more common cases
}
}
这不起作用,因为我们只能在 int、enum 或 String 值上使用 switch。在这一点上,我意识到这是做不到的,即使 enum 值基本上只是隐藏的整数。但我喜欢四处挖掘,该功能看起来非常有用,所以:
private static final EnumSet<Human> DEAD_MAN = EnumSet.of(Human.MAN, Human.DEAD);
public void doStuff(EnumSet human) {
switch (human) {
case DEAD_MAN:
// do something
break;
// more common cases
}
}
仍然没有运气。 Knowing the trick for switch on Strings 并且 EnumSets 实际上是 64 位字段(或它们的数组),我也会尝试:
switch (human.hashCode()) {
case (Human.MAN.hashCode() | Human.DEAD.hashCode()):
// do something
break;
// more common cases
}
认为当 Human hashCode() 被正确实施以提供一致的结果时,它可以工作。没有:
java.lang.Error: 未解决的编译问题:case 表达式必须是常量表达式
现在,我想知道为什么不可能这样做。我一直认为 Java 中的 enums 和 EnumSets 是那些老式位域的合适替代品,但在这里似乎新方法无法处理更复杂的情况。
与switch 的任何一种可能性相比,正确的 解决方案有点糟糕:
public void doStuff(EnumSet human) {
if (human.contains(Human.MAN) && human.contains(Human.DEAD)) {
// do something
} else {
// more common cases
}
}
特别是自从Strings上引入switch之后,我相信switch上EnumSets上至少有两种可能的实现方式:
- 在
case (Human.MAN | Human.DEAD)表达式中,简单地使用编译时类型检查和ordinal()而不是枚举本身。 - 使用the same trick as for Strings。
- 在编译时,计算枚举值的
hashCode()name(可能还有一些额外的东西 - 枚举中的值的数量,ordinal()等 - 一切都是从编译时开始是静态和常量)。是的,这意味着将hashCode()更改为EnumSet类或Enum类。 - 使用而不是枚举本身
- 在编译时,计算枚举值的
现在,是否有任何我没有考虑到的严重障碍(我可以想出一些,都可以轻松克服),这会导致无法轻松实施?还是说这确实是可能的,但对于 Oracle 来说还不足以实现它,因为它不经常使用,这是对的吗?
另外,让我声明这是一个纯粹的学术问题可能没有一个好的答案(不知道,否则我不会问)。如果它被证明是无法回答的,我可能会将其设为社区 wiki。但是,我在任何地方都找不到答案(甚至任何人都在讨论它),所以就这样吧。
【问题讨论】:
-
你试过在JDK中写一个类似于RegularEnumSet的类吗?不幸的是,RegularEnumSet 本身是包私有的,但它在内部将其值存储为 long。您可以编写一个将值存储为 int 的版本(仅限于具有
-
@radai 我想,编译器的改变是不会的。然而,我有时会把它放在我的待办事项上尝试一下,只是为了练习。
-
是的,至少它不会很干净。您的新类(SmallEnumSet?)需要在 java.util 包中。但是暴露内部 int 存储可能会让你打开它。
标签: java enums switch-statement bit-fields enumset