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

发布于 2024-09-25 08:15:33 字数 572 浏览 6 评论 0原文

因此,我正在研究这个具有一些静态常量的类:

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 个案例标签均出现错误。

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

So, I am working on this class that has a few static constants:

public abstract class Foo {
    ...
    public static final int BAR;
    public static final int BAZ;
    public static final int BAM;
    ...
}

Then, I would like a way to get a relevant string based on the constant:

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

However, when I compile, I get a constant expression required error on each of the 3 case labels.

I understand that the compiler needs the expression to be known at compile time to compile a switch, but why isn't Foo.BA_ constant?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

§对你不离不弃 2024-10-02 08:15:33

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

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

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

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

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

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


1 - 常量表达式限制可总结如下。常量表达式 a) 只能使用基元类型和 String,b) 只允许文字类型(null 除外)和常量变量,c) 允许可能带括号的常量表达式作为子表达式,d) 允许除赋值运算符、++--instanceof 之外的运算符,并且 e) 允许类型转换为原始类型或仅 String

请注意,这不包括任何形式的方法或 lambda 调用、new.class.length 或数组下标。此外,由于 a),任何数组值、枚举值、原始包装类型值、装箱和拆箱的使用都被排除。

I understand that the compiler needs the expression to be known at compile time to compile a switch, but why isn't Foo.BA_ constant?

While they are constant from the perspective of any code that executes after the fields have been initialized, they are not a compile time constant in the sense required by the JLS; see §15.28 Constant Expressions for the specification of a constant expression1. This refers to §4.12.4 Final Variables which defines a "constant variable" as follows:

We call a variable, of primitive type or type String, that is final and initialized with a compile-time constant expression (§15.28) a constant variable. Whether a variable is a constant variable or not may have implications with respect to class initialization (§12.4.1), binary compatibility (§13.1, §13.4.9) and definite assignment (§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:


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).

花辞树 2024-10-02 08:15:33

您得到需要常量表达式,因为您将值从常量中遗漏了。尝试:

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

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;
    ...
}
飞烟轻若梦 2024-10-02 08:15:33

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

public static final int TAKE_PICTURE = 1;

而不是

public static int TAKE_PICTURE = 1;

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;
不如归去 2024-10-02 08:15:33

因为这些不是编译时间常数。考虑以下有效代码:

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

您只能在运行时知道BAR的值。

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.

生生不灭 2024-10-02 08:15:33

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

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

来源:
带有枚举的 Switch 语句

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

薄荷→糖丶微凉 2024-10-02 08:15:33

我建议使用以下方式:

public enum Animal {
    DOG("dog"), TIGER("tiger"), LION("lion");
    private final String name;

    Animal(String name) {
        this.name = 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;
         }
     }
}

I recommend using the following way:

public enum Animal {
    DOG("dog"), TIGER("tiger"), LION("lion");
    private final String name;

    Animal(String name) {
        this.name = 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;
         }
     }
}
三人与歌 2024-10-02 08:15:33

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

编辑:2021.01.21

这个答案有点误导,我想澄清一下。

  1. if 替换 switch 语句不应被视为 goto 解决方案,有充分的理由解释为什么 switch这两个概念>如果存在于软件开发中,以及在两者之间进行选择时要考虑的性能问题。
  2. 尽管我提供了针对所出现错误的解决方案,但我的回答没有说​​明问题发生的“原因”,而是提供了解决问题的方法。

在我的具体情况下,使用 if 语句足以解决问题。开发人员应该花时间来决定这是否是解决您当前遇到的问题的正确解决方案。

因此,这个答案应该仅被视为我在第一个回复中所述的特定情况下的解决方法,而绝不是这个问题的正确答案

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

高速公鹿 2024-10-02 08:15:33

如果您在开关盒中使用它,那么您需要在将该值插入开关之前获取枚举的类型。例如 :

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

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

枚举就像:

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

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;
    }
}
少女的英雄梦 2024-10-02 08:15:33

有时开关变量也会犯这个错误,例如:

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

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

   //...
}

要解决这个问题,您应该将变量转换为int(在本例中)。所以:

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

   case 0: //No Error

   //...
}

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

   //...
}
維他命╮ 2024-10-02 08:15:33

就我而言,我得到了这个异常,因为

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

在第二种情况下,我从实例 var.MODIFICAR_KM: 调用常量,但我应该直接从班级。

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.

冰雪梦之恋 2024-10-02 08:15:33

下面的代码是不言自明的,
我们可以使用带有 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;
    }
}

基于可以映射枚举中的类值:

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

    }

希望它有帮助:)

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 :)

吻安 2024-10-02 08:15:33

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

 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

            }

尽管声明了一个常量:

public static Final String ADMIN_CONSTANT= "Admin";

我通过将代码更改为以下内容解决了该问题:

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:

            }

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:

            }
尐籹人 2024-10-02 08:15:33

问题在于 int 和 Integer (我不知道为什么),将 int 更改为 string 并且它可以工作

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

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";
    }
}
那一片橙海, 2024-10-02 08:15:33

只需在“geadle.properties”中添加“android.nonFinalResIds=false”

,所有“需要常量表达式”错误就会消失。

Just add "android.nonFinalResIds=false"

In 'geadle.properties' and all 'Constant expression required' bugs will be gone.

南渊 2024-10-02 08:15:33

我建议您使用枚举:)

看看这个:

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());

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());
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文