Java switch语句:需要常量表达式,但它是常量

Java switch语句:需要常量表达式,但它是常量,java,compile-time-constant,Java,Compile Time Constant,所以,我正在研究这个类,它有几个静态常数: public abstract class Foo { ... public static final int BAR; public static final int BAZ; public static final int BAM; ... } 然后,我想要一种基于常量获取相关字符串的方法: public static String lookup(int constant) { switch (co

所以,我正在研究这个类,它有几个静态常数:

public abstract class Foo {
    ...
    public static final int BAR;
    public static final int BAZ;
    public static final int BAM;
    ...
}
然后,我想要一种基于常量获取相关字符串的方法:

public static String lookup(int constant) {
    switch (constant) {
        case Foo.BAR: return "bar";
        case Foo.BAZ: return "baz";
        case Foo.BAM: return "bam";
        default: return "unknown";
    }
}
然而,当我编译时,我在3个case标签中的每一个上都得到了一个
常量表达式required
错误

我知道编译器需要在编译时知道表达式才能编译开关,但是为什么
Foo.BA
不是常量呢

我知道编译器需要在编译时知道表达式才能编译开关,但是为什么Foo.BA不是常量呢

虽然从初始化字段后执行的任何代码的角度来看,它们都是常量,但在JLS要求的意义上,它们不是编译时常量;有关常量表达式1的规范,请参阅。这是指定义“常量变量”的变量,如下所示:

我们将原语类型或字符串类型的变量称为常量变量,该变量是最终变量,并用编译时常量表达式(§15.28)初始化。变量是否为常量变量可能涉及到类初始化(§12.4.1)、二进制兼容性(§13.1,§13.4.9)和确定赋值(§16)

在您的示例中,Foo.BA*变量没有初始值设定项,因此不符合“常量变量”的条件。解决方法很简单;将Foo.BA*变量声明更改为具有作为编译时常量表达式的初始值设定项

在其他示例中(初始值设定项已经是编译时常量表达式),可能需要将变量声明为
final

您可以将代码更改为使用
enum
而不是
int
常量,但这会带来另外两个不同的限制:

  • 您必须包含一个
    默认值
    大小写,即使对于
    枚举
    的每个已知值都有
    大小写
    ;看
  • case
    标签必须全部是显式的
    enum
    值,而不是计算为
    enum
    值的表达式

1-常量表达式限制可总结如下。常量表达式a)只能使用原语类型和
字符串
,b)只允许原语是文字(除了
null
)和常量变量,c)允许常量表达式可能被括在括号中作为子表达式,d)允许除赋值运算符以外的运算符,
++
--
instanceof
,e)只允许对基元类型或
字符串进行类型转换


请注意,这不包括任何形式的方法或lambda调用、
new
.class
.length
或数组订阅。此外,数组值、
enum
值、基本包装类型的值、装箱和取消装箱都被排除在外,因为它们不是编译时常量。考虑下面的有效代码:

public static final int BAR = new Random().nextInt();
在运行时,您只能知道
条的值。

您将获得所需的常量表达式,因为您将这些值从常量中删除。尝试:

public abstract class Foo {
    ...
    public static final int BAR=0;
    public static final int BAZ=1;
    public static final int BAM=2;
    ...
}

您可以使用如下示例中所示的枚举:

public class MainClass {
enum Choice { Choice1, Choice2, Choice3 }
public static void main(String[] args) {
Choice ch = Choice.Choice1;

switch(ch) {
  case Choice1:
    System.out.println("Choice1 selected");
    break;
 case Choice2:
   System.out.println("Choice2 selected");
   break;
 case Choice3:
   System.out.println("Choice3 selected");
   break;
    }
  }
}
资料来源:
我建议您使用枚举:)

看看这个:

public enum Foo 
{
    BAR("bar"),
    BAZ("baz"),
    BAM("bam");

    private final String description;

    private Foo(String description)
    {
        this.description = description;
    }

    public String getDescription()
    {
        return description;
    }
}
然后你可以这样使用它:

System.out.println(Foo.BAR.getDescription());
 roleSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

            switch (parent.getItemAtPosition(position)) {
                case ADMIN_CONSTANT: //Threw the error

            }

我在Android上遇到了这个错误,我的解决方案就是使用:

public static final int TAKE_PICTURE = 1;
而不是

public static int TAKE_PICTURE = 1;

这是很久以前的回答,可能不相关,但只是以防万一。 当我遇到这个问题时,我只使用了
if
语句而不是
switch
,它解决了错误。 这当然是一个解决办法,可能不是“正确”的解决方案,但在我的情况下,这就足够了

编辑:2021.01.21 这个答案有点误导,我想澄清一下

  • 使用<代码>替换<代码>开关>代码>语句>不应被视为GOTO解决方案,在软件开发中存在“代码>开关和<代码>如果两个概念的非常好的原因,以及在两者之间选择时要考虑的性能问题。
  • 虽然我提供了一个解决上述错误的方法,但我的回答并没有阐明问题发生的“原因”,而是提供了解决问题的方法
  • 在我的具体案例中,使用
    if
    语句就足以解决问题。开发人员应该花点时间来决定这是否是当前问题的正确解决方案


    因此,在我第一次回答中所述的特定情况下,此答案应仅被视为一种变通方法,而决不是此问题的正确答案

    有时开关变量也会导致该错误,例如:

    switch(view.getTag()) {//which is an Object type
    
       case 0://will give compiler error that says Constant expression required
    
       //...
    }
    
    要解决此问题,应将变量强制转换为int(在本例中)。因此:


    在Android中执行以下操作时出现此错误:

    System.out.println(Foo.BAR.getDescription());
    
     roleSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
    
                switch (parent.getItemAtPosition(position)) {
                    case ADMIN_CONSTANT: //Threw the error
    
                }
    

    就我而言,我得到这个例外是因为

    switch (tipoWebServ) {
                                case VariablesKmDialog.OBTENER_KM:
                                    resultObtenerKm(result);
                                    break;
                                case var.MODIFICAR_KM:
                                    resultModificarKm(result);
                                    break;
                            }
    

    在第二种情况下,我从实例
    var.MODIFICAR_KM:
    调用常量,但我应该直接从类中使用
    VariablesKmDialog.OBTENER_KM

    如果在开关情况下使用它,那么即使在将该值插入开关之前,也需要获取枚举的类型。例如:

    SomeEnum SomeEnum=SomeEnum.values()[1]

    枚举如下所示:

    public enum SomeEnum {
    
        GRAPES("Grapes", 0),
        BANANA("Banana", 1),
    
        private String typeName;
        private int typeId;
    
        SomeEnum(String typeName, int typeId){
            this.typeName = typeName;
            this.typeId = typeId;
        }
    }
    

    下面的代码是不言自明的, 我们可以将枚举与开关案例一起使用:

    /**
     *
     */
    enum ClassNames {
        STRING(String.class, String.class.getSimpleName()),
        BOOLEAN(Boolean.class, Boolean.class.getSimpleName()),
        INTEGER(Integer.class, Integer.class.getSimpleName()),
        LONG(Long.class, Long.class.getSimpleName());
        private Class typeName;
        private String simpleName;
        ClassNames(Class typeName, String simpleName){
            this.typeName = typeName;
            this.simpleName = simpleName;
        }
    }
    
    基于类,可以映射枚举中的值:

     switch (ClassNames.valueOf(clazz.getSimpleName())) {
            case STRING:
                String castValue = (String) keyValue;
                break;
            case BOOLEAN:
                break;
            case Integer:
                break;
            case LONG:
                break;
            default:
                isValid = false;
    
        }
    

    希望有帮助:)

    我建议使用以下方法:

    public enum Animal {
        DOG("dog"), TIGER("tiger"), LION("lion");
        private final String name;
    
        @Override
        public String toString() {
            return this.name;
        }
    }
    
    
    public class DemoSwitchUsage {
    
         private String getAnimal(String name) {
             Animal animalName = Animal.valueOf(name);
             switch(animalName) {
             case DOG:
                 // write the code required.
                 break;
             case LION:
                 // Write the code required.
                 break;
             default:
                 break;
             }
         }
    }
    

    有趣。
    public static final int BAR=new Random().nextInt()
    有效吗?Thilo的语句可以编译,但switch语句需要常量表达式。此外,两个连续的
    new Random().nextInt()
    不能返回相同的值吗?@Tony:这是一件好事。它不是c
    public enum Animal {
        DOG("dog"), TIGER("tiger"), LION("lion");
        private final String name;
    
        @Override
        public String toString() {
            return this.name;
        }
    }
    
    
    public class DemoSwitchUsage {
    
         private String getAnimal(String name) {
             Animal animalName = Animal.valueOf(name);
             switch(animalName) {
             case DOG:
                 // write the code required.
                 break;
             case LION:
                 // Write the code required.
                 break;
             default:
                 break;
             }
         }
    }