旧的方式,如果我们想要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
and 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
}
}
这是行不通的,因为我们只能switch
使用int
,enum
或String
值。在这一点上,我意识到这是不可能的,即使这些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
}
}
仍然没有运气。知道打开字符串的技巧并且 EnumSets 实际上是 64 位字段(或它们的数组),我也会尝试:
switch (human.hashCode()) {
case (Human.MAN.hashCode() | Human.DEAD.hashCode()):
// do something
break;
// more common cases
}
认为当Human
hashCode()
正确实施以产生一致的结果时,它可能会起作用。没有:
java.lang.Error:未解决的编译问题:case 表达式必须是常量表达式
现在,我想知道为什么没有可能做到这一点。我一直认为,enums
在EnumSets
Java 中,它是那些老式位域的适当替代品,但在这里,新方法似乎无法处理更复杂的情况。
与任何可能性相比,正确的解决方案很糟糕:switch
public void doStuff(EnumSet human) {
if (human.contains(Human.MAN) && human.contains(Human.DEAD)) {
// do something
} else {
// more common cases
}
}
特别是,自从引入了switch
on ,我相信onStrings
至少有两种可能的实现:switch
EnumSets
- 在
case (Human.MAN | Human.DEAD)
表达式中,简单地使用编译时类型检查而ordinal()
不是枚举本身。 - 使用与 Strings 相同的技巧。
- 在编译时,计算枚举值的 (可能还有一些额外的东西 - 枚举中的值的数量
hashCode()
等name
ordinal()
- 从编译时开始,一切都是静态和恒定的)。是的,这将意味着更改班级或hashCode()
班级。EnumSet
Enum
- 使用而不是枚举本身
- 在编译时,计算枚举值的 (可能还有一些额外的东西 - 枚举中的值的数量
现在,是否有任何我没有考虑到的严重障碍(我可以想出一些,都可以很容易地克服)会使这不可能轻松实现?或者我认为这确实是可能的,但对于 Oracle 来说还不足以实现它,因为它不经常使用,这是对的吗?
另外,让我声明这是一个纯粹的学术问题,可能没有好的答案(不知道,否则我不会问)。如果它被证明是无法回答的,我可能会将其设为社区 wiki。但是,我在任何地方都找不到答案(甚至任何人都在讨论它),所以就这样吧。