Guard variable and being effectively final

Remi Forax forax at
Sat May 21 11:55:52 UTC 2022

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

I get the following error

java --enable-preview --source 19 error: local variables referenced from a guard must be final or effectively final
        case String __ && (op = OPS.get(token)) != null -> {
Note: 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.


More information about the amber-spec-experts mailing list