在 switch-case 语句中使用 Enum 的序数值
对于我的项目,我使用枚举,并且需要实现 switch-case 语句,其中检查特定枚举的值的序数,如下所示:
switch ( variable )
{
case MyEnum.A.ordinal():
return true;
case MyEnum.B.ordinal():
return true;
default:
return false;
}
注意:返回值只是一个示例。
不幸的是,Eclipse(我使用的是 1.6 JDK)给出了我的编译错误“case 表达式必须是常量表达式”。我应该做什么?除了静态查找表之外还有其他方法吗,如下所述: 从枚举序数转换为枚举类型 ?
For my project I am using enums, and I need to implement the switch-case statement, where ordinal numbers of values of the specific Enum are checked, like this way:
switch ( variable )
{
case MyEnum.A.ordinal():
return true;
case MyEnum.B.ordinal():
return true;
default:
return false;
}
Note: return values are only an example.
Unfortunately, Eclipse (I'm using 1.6 JDK) gives my compilation error "case
expressions must be constant expressions". What I should do? Is there any other method than static lookup table, described here: Convert from enum ordinal to enum type ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

发布评论
评论(7)
假设您在某处以某种方式有一个序列化序数,这就是完成的方法。不过,持久化枚举的通常方法是通过其名称,而不是序号。另外,在正常情况下你不应该使用序数,除非尝试实现类似 EnumMap/Set 的东西。当然,枚举可以只是 C 类似内容的一个端口,并且处理不可避免的 int,需要转换为 Enum 对象。
只需使用 Enum.values()
即可获取按 ordinal()
排序的数组,因为每次都会克隆该数组,因此保留一个引用即可。
enum E{
A, B, C...
}
final static E[] vals = E.values();//copy the values(), calling values() clones the array
boolean f(int variable){
switch(vals[variable]){
case A:
...
case B:
...
//break;
default:
...
}
}
只是注意到你只需要 true 和 false,这是一种 Set 类型的行为。如果您足够勇敢(并且枚举常量不超过 64 个),您可以使用 java.util.EnumSet 或简单的 long
。例如:
private static <E extends Enum> long ord(E e){
return 1L<<e.ordinal();
}
static final long positiveSet = ord(E.A)+ord(E.B);
boolean f(int ordinal){
return 0!=(positiveSet&(1L<<ordinal));
}
答案针对 @Riaan 对常量与方法枚举和性能原因的评论,它并没有直接回答OP问题,所以我想它可以被认为是噪音。
然而,我相信了解内部运作方式是很重要的。
我从他的示例中获取了基准并对其进行了改进,以消除占用 90% 以上执行时间的垃圾收集和字符串创建。添加了预热阶段以确保热点实际编译方法。
还有更多,基准测试是有效的调用站点测试。对于 1、2、少数更多和更多更多,调用站点的优化有很大不同。调用点是对抽象(或只是重写)方法的调用。
下面是使用 6 个枚举常量的测试:
package t1;
public class ZEnums {
public enum MyEnum {
A { boolean getBooleanValue(){ return true; }},
B { boolean getBooleanValue(){ return true; }},
C { boolean getBooleanValue(){ return false; }},
D { boolean getBooleanValue(){ return false; }},
E { boolean getBooleanValue(){ return false; }},
F { boolean getBooleanValue(){ return false; }},
;
abstract boolean getBooleanValue();
}
public enum MyEnumAlt {
A (true),
B (true),
C (false),
D (false),
E (false),
F (false),
;
private final boolean isTrue;
MyEnumAlt( boolean isTrue){ this.isTrue = isTrue; }
boolean getBooleanValue(){ return isTrue; };
}
public static void main(String[] args) {
log("Warming up...");
//10k iterations won't do since not all paths for MyEnum are invoked 10k (default) times to warrant compilations
long warmum = testEnum(100000 )+ testAlt(100000)+testEnum(100000 )+ testAlt(100000);
log("Warm up: %d", warmum);
//no info from +XX:+PrintCompilation below this one, or the test is invalid
testMain();
}
public static void testMain() {
int iterations = (int)4e7;
log("Testing %d iterations%n", iterations);
log("====");
log("Testing with Overridden method...");
System.gc();
{
long start = System.currentTimeMillis();
long len = 0;
len = testEnum(iterations);
long time = System.currentTimeMillis()-start;
log("Overridden method version took %dms, length: %d ", time, len);
}
////////////
System.gc();
{
log("Testing with Constant in c-tor... ");
long start = System.currentTimeMillis();
long len = testAlt(iterations);
long time = System.currentTimeMillis()-start;
log("Constant in c-tor version took %dms, length: %d ", time, len);
}
}
private static long testEnum(int iterations) {
long len = 0;
for(int i=0; i<iterations; i++){
MyEnum tmpEnum = MyEnum.A;
if(i%3==0){ tmpEnum = MyEnum.A;
}else if(i%4==0){ tmpEnum = MyEnum.B;
}else if(i%5==0){ tmpEnum = MyEnum.C;
}else if(i%6==0){ tmpEnum = MyEnum.D;
}else if(i%6==0){ tmpEnum = MyEnum.E;
}else{ tmpEnum = MyEnum.F;
}
String tmp = tmpEnum.getBooleanValue()?"XXX":"ABCDE";
len+=tmp.length();
}
return len;
}
private static long testAlt(int iterations) {
long len =0;
for(int i=0; i<iterations; i++){
MyEnumAlt tmpEnum = MyEnumAlt.A;
if(i%3==0){ tmpEnum = MyEnumAlt.A;
}else if(i%4==0){ tmpEnum = MyEnumAlt.B;
}else if(i%5==0){ tmpEnum = MyEnumAlt.C;
}else if(i%6==0){ tmpEnum = MyEnumAlt.D;
}else if(i%6==0){ tmpEnum = MyEnumAlt.E;
}else{ tmpEnum = MyEnumAlt.F;
}
String tmp = tmpEnum.getBooleanValue()?"XXX":"ABCDE";
len+=tmp.length();
}
return len;
}
static void log(String msg, Object... params){
String s = params.length>0?String.format(msg, params):msg;
System.out.printf("%tH:%<tM:%<tS.%<tL %s%n", new Long(System.currentTimeMillis()), s);
}
}
21:08:46.685 Warming up... 148 1% t1.ZEnums::testEnum @ 7 (125 bytes) 150 1 t1.ZEnums$MyEnum$6::getBooleanValue (2 bytes) 152 2 t1.ZEnums$MyEnum$1::getBooleanValue (2 bytes) 154 3 t1.ZEnums$MyEnum$2::getBooleanValue (2 bytes) 155 4 t1.ZEnums$MyEnum$3::getBooleanValue (2 bytes) 158 2% t1.ZEnums::testAlt @ 7 (125 bytes) 162 5 t1.ZEnums::testEnum (125 bytes) 164 6 t1.ZEnums::testAlt (125 bytes) 21:08:46.716 Warm up: 1600000 21:08:46.716 Testing 40000000 iterations 21:08:46.716 ==== 21:08:46.716 Testing with Overridden method... 21:08:47.513 Overridden method version took 781ms, length: 160000000 21:08:47.513 Testing with Constant in c-tor... 21:08:48.138 Constant in c-tor version took 625ms, length: 160000000
代码使用 -server -XX:+PrintCompilation
选项运行。
当然,差别并不大。但这不是有趣的问题。如果您使用 2 个枚举常量测试该版本,结果可能会显着不同。对于 2 个调用站点,编译器通过内联相关方法来生成代码。在上面的测试中,这将删除对 booleanValue 的整个调用,甚至可以在 O(1) 中执行测试。
然而,最有趣的部分是,当编译器开始使用内联缓存时,枚举常量从 2 个变为 3 个,然后是常量,哇哦,一切都发生了变化。
The bottom line is: proper benchmark is truly hard and involves some knowledge how the JIT compiles, when the GC might be an issue (either remove it or embrace it) and so on.
Links:
你所要求的可能是:
如果您需要枚举本身的方法中的 switch:
switch ( this )
{
case A:
return true;
case B:
return true;
default:
return false;
}
并且在不同的类中:
switch ( variable ) //Variable of type myEnum
{
case A:
return true;
case B:
return true;
default:
return false;
}
如果添加另一个枚举,很容易忘记更新 switch 语句,因此更好的选择是将这样的方法放在枚举本身中并使用特定于常量的方法实现:
public enum MyEnum
A { boolean getBooleanValue(){ return true; },
B { boolean getBooleanValue(){ return true; },
C { boolean getBooleanValue(){ return false; };
abstract boolean getBooleanValue();
}
这样,如果您添加新的枚举值,编译器将提醒您声明 getBooleanValue 方法,您只需在需要的地方使用 A.getBooleanValue();
它。
正如评论中指出的,另一种选择是:
public enum MyEnumAlt {
A (true),
B (true),
C (false);
private final boolean isTrue;
MyEnumAlt( boolean isTrue){ this.isTrue = isTrue; }
boolean getBooleanValue(){ return isTrue; };
}
这是一个偏好问题,会根据具体情况而有所不同。如果您只是为每个枚举返回一个值,则构造函数版本是合理的,但我发现它的可读性较差。通过测试可以看出,对这种性能更好的担忧是没有根据的:
public void testMain() {
System.out.println("Testing with constructor: ");
long start = System.currentTimeMillis();
for(int i=0; i<1000*1000; i++){
MyEnum tmpEnum = null;
if(i%3==0){ tmpEnum = MyEnum.A;
}else if(i%4==0){ tmpEnum = MyEnum.B;
}else{ tmpEnum = MyEnum.C; }
String tmp = Integer.toString(i)+" "+tmpEnum.getBooleanValue();
}
long time = System.currentTimeMillis()-start;
System.out.println("Constructor version took "+time);
System.out.println("Testing with Constant specific method implementation: ");
long start2 = System.currentTimeMillis();
for(int i=0; i<1000*1000; i++){
MyEnumAlt tmpEnum2 = null;
if(i%3==0){ tmpEnum2 = MyEnumAlt.A;
}else if(i%4==0){ tmpEnum2 = MyEnumAlt.B;
}else{ tmpEnum2 = MyEnumAlt.C; }
String tmp2 = Integer.toString(i)+" "+tmpEnum2.getBooleanValue();
}
long time2 = System.currentTimeMillis()-start2;
System.out.println("Constant specific method version took "+time2);
}
更好的解决方案是这样的:
enum:
public interface ACServices {
public static enum MessageType {
// periodic needs to saved in DB
PIPE_INFO_TYPE_AC_DEVICE_LIST, // periodic from littlecloud
PIPE_INFO_TYPE_DEV_ONLINE,
PIPE_INFO_TYPE_DEV_OFFLINE,
PIPE_INFO_TYPE_EVENT_LOG,
PIPE_INFO_TYPE_DEV_DETAIL,
};
implementation:
ACServices.MessageType msgType = ACServices.MessageType.valueOf(acResponse.getType());
switch (msgType){
case INT_INFO_DEV_STATUS:
break;
case INT_INFO_DEV_TZ:
break;
case PIPE_INFO_DEV_COUNT:
break;
case PIPE_INFO_TYPE_AC_DEVICE_LIST:
break;
case PIPE_INFO_TYPE_CONFIG_GET_TEXT:
break;
default:
break;
}
Man Pak Hong, Dave ([电子邮件受保护])
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
这是因为编译器会看到差异。例如这个枚举代码,我们可以看到:
但是编译器看到:
这就是为什么当交通灯为 NULL 时它会抛出 NPE,并且您也不知道为什么它在 ordinal() 函数中抛出 NPE,即使我们没有调用该方法。
解决方案是在 ENUM 到达 switch-case 之前检查 NULL。
It is because the compiler sees differences. For example this enum code, we can see:
BUT the compiler see:
That's why it throws NPE when trafficLights are NULL and you also don't know why it throws NPE in the ordinal() function even though we didn't call that method.
SOLUTION is checking NULL the ENUM before it reaches to the switch-case.