在 Java 17 switch-case 中使用 final 变量的好处

Benefit of using final variables in Java 17 switch-case

谁能告诉我 Java 17 接受最终表达式作为 switch-case-constructs 中的 case 表达式,但不接受最终表达式作为参数传递的好处?

    void test(int distinction, final int foo) {
        final var bar = 2;
        
        switch (distinction) {
        case foo -> doSomething(); // does not compile -> case expressions must be constant expressions
        case bar -> doSomething(); // does compile
        case 3 -> doOtherThings(); // does compile
        }
    }

为什么编译器不接受情况 1,尽管 foo 和 bar 一样是最终变量?

在我看来,案例 3 的可读性比案例 2 好得多。所以我看不到新语言结构的好处。

案例标签必须是编译时间常数。最终参数不是编译时常量;它可能不会在给定的方法调用中发生变化,但它可以在方法的调用中发生变化。 (最终实例字段和没有初始化器的静态最终字段也不是编译时常量。)

您的陈述“所以我看不到新语言结构的好处”包含错误的假设,即涉及新的语言结构。

自 Java 1.0 以来,definition of compile-time constants 没有改变。

A constant variable is a final variable of primitive type or type String that is initialized with a constant expression (§15.29).

因此,与流行的顽固神话相反,编译时常量不一定是 static,也根本不需要是字段。用常量表达式初始化的 final 局部变量是编译时常量。相反,一个从来没有初始化器的参数永远不可能是编译时常量。

此外,switch 标签在整数类型时必须是编译时常量的规则从未改变。这更难识别,因为添加了对其他案例标签的支持,Java 5 引入了切换 enum 类型的可能性,Java 7 添加了对切换 String 的支持] 值,新的模式匹配将允许切换类型。在 enum 或类型标签的情况下,大小写标签不是编译时常量,而是不变的符号名称,因此在切换 enum 或类型时根本不能使用变量名称。

所以下面的程序在每个 Java 版本中都有效:

class SwitchTest {
    public static void main(String[] args) {
        final int one = 1, two = 2;
        switch(args.length) {
          case one: System.out.println("one"); break;
          case two: System.out.println("two"); break;
          case one + two: System.out.println("three"); break;
        }
    }
}

这里不需要实际用例。拥有简单一致的规则比拥有试图排除看似无用的组合的复杂规则要好。

作为补充,由于Java5,引入了注解,以下为合法代码

class Test {
    @interface Example { String value(); }
    public static void main(String[] args) {
        final String str = "test";
        @Example(str) class Local {}
    }
}

这证明了规则的一致性。注释要求值是编译时常量,因此如果变量是编译时常量,则可以在注释中使用局部变量。