JotA
JotA

Reputation: 23

Benefit of using final variables in Java 17 switch-case

Can someone tell me the benefit of Java 17 accepting final expressions as a case expression in switch-case-constructs, but does not accept the final expression to be passed as a parameter?

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

Why doesn't the compiler accept case 1, although foo is a final variable as bar is?

In my opinion the readability of case 3 is much better than case 2. So I do not see the benefit of the new language construct.

Upvotes: 2

Views: 1936

Answers (2)

Holger
Holger

Reputation: 298469

Your statement “So I do not see the benefit of the new language construct” contains the wrong assumption that there was a new language construct involved.

The definition of compile-time constants has not changed since Java 1.0.

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

So contrary to popular tenacious myths, a compile-time constant doesn’t have to be static nor does it need to be a field at all. A final local variable, initialized with a constant expression is a compile time constant. In contrast, a parameter which never has an initializer can never be a compile-time constant.

Further, the rule that switch labels have to be compile-time constants when being of an integer type never changed. This is harder to recognize, as support for other case labels have been added, Java 5 introduced the possibility to switch over enum types, Java 7 added support for switching over String values, and the new pattern matching will allow to switch over types. In case of enum or type labels, the case labels are not compile-time constants, but invariant symbolic names, so you can’t use variable names at all when switching over an enum or type.

So the following program works in every Java version:

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

There is no need for practical use cases here. Having simple and consistent rules is better than having complicated rules which try to exclude combinations that appear to be of no use.

As an addendum, since Java 5, which introduced annotations, the following is legal code

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

which demonstrates the consistency of the rules. Annotations require the values to be compile-time constants, so using a local variable in an annotation is possible, if the variable is a compile-time constant.

Upvotes: 2

Brian Goetz
Brian Goetz

Reputation: 95476

Case labels must be compile time constants. A final parameter is not a compile-time constant; it may not vary across a given invocation of the method, but it can vary across invocations of the method. (Final instance fields, and static final fields without an initializer, are also not compile-time constants.)

Upvotes: 12

Related Questions