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

2022-08-31 06:01:24

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

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个案例标签中的每个标签上都收到错误。constant expression required

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


答案 1

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

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

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

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

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

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

  • 您必须包括一个事例,即使您具有 ; 的每个已知值请参阅为什么枚举上的开关需要默认值?defaultcaseenum
  • 标签必须全部是显式值,而不是计算结果为值的表达式。caseenumenum

1 - 常量表达式限制可归纳如下。常量表达式 a) 只能使用基元类型和 String,b) 允许作为文本(null 除外)和仅常量变量的原表达式,c) 允许常量表达式可能用括号括起来作为子表达式,d) 允许除赋值运算符 ++--instanceof 以外的运算符,以及 e) 允许将类型转换为基元类型或仅字符串

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


答案 2

您得到常量表达式必需,因为您省略了常量的值。尝试:

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

推荐