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