一个Switch Java问题:case表达式必须是常量表达式。

26

我在 switch/case 语句中遇到了问题。错误提示是:"Case expressions must be constant expressions"。我理解这个错误,并且可以使用 if 语句来解决它,但是有人能告诉我为什么 switch/case 中的 case 表达式必须是常量表达式吗?

public boolean onOptionsItemSelected(MenuItem item) {
    int idDirectory = ((MenuItem) findViewById(R.id.createDirectory)).getItemId();
    int idSuppression = ((MenuItem) findViewById(R.id.recycleTrash)).getItemId();
    int idSeeTrash = ((MenuItem) findViewById(R.id.seeTrash)).getItemId();

    switch (item.getItemId()) {
    case idDirectory:
        createDirectory(currentDirectory);
        break;
    case idSuppression:
        recycleTrash();
        break;
    case idSeeTrash:
        seeTrash();
        break;
    }

    return super.onOptionsItemSelected(item);
}

感谢您的解释!!


5
为什么要使用 findViewById().getItemId()?如果你已经有了id,就不需要获取item来获取id了! - Anon.
2
switch语句是一种不好的代码味道...我建议你考虑使用多态或适配器模式,以实现更好的代码外观。此外,你可以完全避免这些问题。 - Bnjmn
@Bnjmn 我同意,特别是当条件经常重复时。如果你很少使用它,那么可能还好。此外,我有时会使用多态性来创建初始的具体实例。 - OscarRyz
2
@Bnjmn:由于Dimitri正在使用Android,他的MenuItem对象可能是在xml中定义的,而且(据我所知)如果不放弃MenuInflater.inflate()的简单性,就无法轻松地对其进行子类化。 - idbrii
3个回答

55

这样可以在编译阶段进行评估(静态检查)。

详见:http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.11,了解switch的正式定义。

此外,可能有助于更好地理解该switch如何转换为字节码:

class Switch {
  void x(int n ) {
    switch( n ) {
      case 1: System.out.println("one"); break;
      case 9: System.out.println("nine"); break;
      default:  System.out.println("nothing"); break;
    }
  }
}

编译后:

C:\>javap -c Switch
Compiled from "Switch.java"
class Switch extends java.lang.Object{
Switch();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

void x(int);
  Code:
   0:   iload_1
   1:   lookupswitch{ //2
                1: 28;
                9: 39;
                default: 50 }
   28:  getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   31:  ldc     #3; //String one
   33:  invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   36:  goto    58
   39:  getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   42:  ldc     #5; //String nine
   44:  invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   47:  goto    58
   50:  getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   53:  ldc     #6; //String nothing
   55:  invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   58:  return

}

看到那行标记为1:

 1:   lookupswitch{ //2
            1: 28;
            9: 39;
            default: 50 }

它会评估这个值并跳到另一行代码。例如,如果值是9,它将跳到第39行指令:


   39:  getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   42:  ldc     #5; //String nine
   44:  invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   47:  goto    58

接着跳转到指令58:

   58:  return

如果这是动态评估,这一切都不可能实现。这就是原因。


谢谢,伙计,这是一个很好的答案。所以如果使用javap编译,我可以看到生成的字节码吗? - Dimitri
1
@Dimitri。不用谢。javap是另一个工具(像javadoc或jar),它是默认的java反汇编器。如果使用-c调用它,您将看到这些指令。 - OscarRyz
好的。但是我在想,你觉得JVM生成的字节码和Dalvik虚拟机(Android)中生成的字节码是一样的吗?我正在开发Android应用,你可能已经注意到了。 - Dimitri
实际上...它们并不相同。Dalvik有自己的代码,但在两者中开关的工作方式是相同的。请仔细查看:http://en.wikipedia.org/wiki/Dalvik_(software) - OscarRyz
很好的回答,清楚地理解了switch case的工作原理。值得点赞。 - Muhammad Tariq
很好的答案,清楚地理解了switch case的工作原理。值得点赞。 - undefined

13

3

idDirectory等需要是常量而不是声明变量。在这种情况下,switch将无法正常工作,你需要使用if-else结构。

编辑 我明白OP的意思了。这就是java语言中switch的工作方式。


这正是你的问题。如果你无法通过常量来解决它,只需使用稍微复杂一些的if-then-else-if-blah-blah设置即可。 - Will Tate
7
“我理解这个错误,知道可以使用 if 语句来解决,但是为什么在 switch/case 中 case 表达式必须是常量呢?” - OscarRyz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接