202

所以,我正在研究这个有一些静态常量的类:

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";
    }
}

但是,当我编译时,constant expression required3 个案例标签中的每一个都出现错误。

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

4

14 回答 14

174

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

虽然从初始化字段后执行的任何代码的角度来看,它们是常量,但它们不是 JLS 要求的编译时间常量有关常量表达式1的规范,请参见第 15.28 节常量表达式。这指的是§4.12.4 最终变量,它定义了一个“常量变量”,如下所示:

我们将原始类型或 String 类型的变量称为 final 并使用编译时常量表达式(第 15.28 节)初始化的变量为常量变量。变量是否为常量变量可能对类初始化(§12.4.1)、二进制兼容性(§13.1、§13.4.9)和明确赋值(§16)有影响。

In your example, the Foo.BA* variables do not have initializers, and hence do not qualify as "constant variables". The fix is simple; change the Foo.BA* variable declarations to have initializers that are compile-time constant expressions.

In other examples (where the initializers are already compile-time constant expressions), declaring the variable as final may be what is needed.

You could change your code to use an enum rather than int constants, but that brings another couple of different restrictions:

  • You must include a default case, even if you have case for every known value of the enum; see Why is default required for a switch on an enum?
  • The case labels must all be explicit enum values, not expressions that evaluate to enum values.

1 - The constant expression restrictions can be summarized as follows. Constant expressions a) can use primitive types and String only, b) allow primaries that are literals (apart from null) and constant variables only, c) allow constant expressions possibly parenthesised as subexpressions, d) allow operators except for assignment operators, ++, -- or instanceof, and e) allow type casts to primitive types or String only.

Note that this doesn't include any form of method or lambda calls, new, .class. .length or array subscripting. Furthermore, any use of array values, enum values, values of primitive wrapper types, boxing and unboxing are all excluded because of a).

于 2010-09-30T03:11:32.373 回答
83

You get Constant expression required because you left the values off your constants. Try:

public abstract class Foo {
    ...
    public static final int BAR=0;
    public static final int BAZ=1;
    public static final int BAM=2;
    ...
}
于 2010-09-30T03:28:48.460 回答
54

I got this error on Android, and my solution was just to use:

public static final int TAKE_PICTURE = 1;

instead of

public static int TAKE_PICTURE = 1;
于 2015-02-25T21:16:47.277 回答
37

Because those are not compile time constants. Consider the following valid code:

public static final int BAR = new Random().nextInt();

You can only know the value of BAR in runtime.

于 2010-09-30T03:13:48.767 回答
18

You can use an enum like in this example:

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;
    }
  }
}

Source: Switch statement with enum

于 2011-07-21T14:27:18.860 回答
10

I recommend using the following way:

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;
         }
     }
}
于 2019-12-02T16:50:39.207 回答
6

This was answered ages ago and probably not relevant, but just in case. When I was confronted with this issue, I simply used an if statement instead of switch, it solved the error. It is of course a workaround and probably not the "right" solution, but in my case it was just enough.

Edit: 2021.01.21

This Answer is a bit misleading, And I would like to clarify it.

  1. Replacing a switch statement with an if should not be considered as a goto solution, there are very good reasons why both concepts of switch and if exist in software development, as well as performance matters to consider when choosing between the two.
  2. Although I provide a solution to the presented error, My answer sheds no light on "why" the problem occurs but instead offers a way around the problem.

In my specific case, using an if statement instead was just enough to solve the problem. Developers should take the time and decide if this is the right solution for the current problem you have at hand.

Thus this answer should be considered solely as a workaround in specific cases as stated in my first response, and by no means as the correct answer to this question

于 2018-02-15T18:35:35.387 回答
2

If you're using it in a switch case then you need to get the type of the enum even before you plug that value in the switch. For instance :

SomeEnum someEnum = SomeEnum.values()[1];

switch (someEnum) {
            case GRAPES:
            case BANANA: ...

And the enum is like:

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;
    }
}
于 2019-09-18T16:52:19.163 回答
1

In my case, I was getting this exception because

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

in the second case I was calling the constant from the instance var.MODIFICAR_KM: but I should use VariablesKmDialog.OBTENER_KM directly from the class.

于 2018-12-04T16:43:35.927 回答
1

Below code is self-explanatory, We can use an enum with a switch case:

/**
 *
 */
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;
    }
}

Based on the class values from the enum can be mapped:

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

    }

Hope it helps :)

于 2019-10-21T08:00:01.013 回答
0

Sometimes the switch variable can also make that error for example:

switch(view.getTag()) {//which is an Object type

   case 0://will give compiler error that says Constant expression required

   //...
}

To solve you should cast the variable to int(in this case). So:

switch((int)view.getTag()) {//will be int

   case 0: //No Error

   //...
}
于 2018-05-14T19:58:16.857 回答
0

Got this error in Android while doing something like this:

 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

            }

despite declaring a constant:

public static final String ADMIN_CONSTANT= "Admin";

I resolved the issue by changing my code to this:

roleSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

            String selectedItem = String.valueOf(parent.getItemAtPosition(position));
            switch (selectedItem) {
                case ADMIN_CONSTANT:

            }
于 2018-11-24T09:25:11.487 回答
0

The problem is with the int and Integer (i do not why), change the int to string and it work's

public abstract class Foo {
    ...
    public static final String BAR;
    public static final String BAZ;
    public static final String BAM;
    ...
}
public static String lookup(String constant) {
    switch (constant) {
        case Foo.BAR: return "bar";
        case Foo.BAZ: return "baz";
        case Foo.BAM: return "bam";
        default: return "unknown";
    }
}
于 2021-10-28T19:57:51.527 回答
-1

I recommend you to use enums :)

Check this out:

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;
    }
}

Then you can use it like this:

System.out.println(Foo.BAR.getDescription());
于 2011-07-21T17:18:19.780 回答