Guard variable and being effectively final

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Mon May 23 17:45:03 UTC 2022


The compiler behavior seems to be in sync with the spec:

 From [1]:

"Any variable that is used but not declared in the guarding expression 
of a guarded pattern must either be final or effectively final (4.12.4)."

And, from [2]:

"Any variable that is used but not declared in a |when| expression must 
be either final or effectively final (4.12.4 
<https://docs.oracle.com/javase/specs/jls/se18/html/jls-4.html#jls-4.12.4>)"

As for your question of "why doesn't it work", I think it can be 
decomposed into two questions:

* why is a "when" expression restricted to only mention 
final/effectively-final variables?
* even under the constraint of final/effectively final, why isn't "when" 
allowed to "initialize" a final variable?

Both of these add some asymmetries when it comes to refactoring the 
switch into a chain of if/else.

Maurizio

[1]: 
https://docs.oracle.com/javase/specs/jls/se18/preview/specs/patterns-switch-jls.html#jls-6.3.3.1
[2]: 
http://cr.openjdk.java.net/~gbierman/PatternSwitchPlusRecordPatterns/PatternSwitchPlusRecordPatterns-20220407/specs/patterns-switch-jls.html

On 21/05/2022 12:55, Remi Forax wrote:
> Not sure if it's an implementation bug (bad error message from the compiler) or a spec bug,
> hence this message to both amber-dev and amber-spec-experts.
>
> If i try to compile this code with Java 19 (which currently still uses && instead of when for a guard)
>
> interface reverse_polish_notation {
>    static Map<String, IntBinaryOperator> OPS =
>        Map.of("+", (a, b) -> a + b, "*", (a, b) -> a * b);
>
>    static int eval(List<String> expr) {
>      var stack = new ArrayDeque<Integer>();
>      for(var token: expr) {
>        final IntBinaryOperator op;
>        stack.push(switch (token) {
>          case String __ && (op = OPS.get(token)) != null -> {
>            var value1 = stack.pop();
>            var value2 = stack.pop();
>            yield op.applyAsInt(value1, value2);
>          }
>          default -> Integer.parseInt(token);
>        });
>      }
>      return stack.pop();
>    }
>
>    static void main(String[] args) {
>      var expr = List.of("1",  "2",  "+", "3", "*", "4");
>      System.out.println(eval(expr));
>    }
> }
>
> I get the following error
>
> java --enable-preview --source 19 reverse_polish_notation.java
> reverse_polish_notation.java:17: error: local variables referenced from a guard must be final or effectively final
>          case String __ && (op = OPS.get(token)) != null -> {
>                             ^
> Note: reverse_polish_notation.java uses preview features of Java SE 19.
> Note: Recompile with -Xlint:preview for details.
> 1 error
> error: compilation failed
>    
> Obviously the error message is funny, IntBinaryOperator is declared final so it is effectively final.
>
> In case of a lambda,
>    final IntBinaryOperator op;
>    Supplier<String> supplier = () -> op = null;
>
> supplier.get() can be called several times so "op = null" does not compile.
>
> But in the example above, "op" can not be assigned more than once so maybe it should compile.
>
> regards,
> Rémi


More information about the amber-dev mailing list