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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20220523/af03e1e7/attachment.htm>
More information about the amber-spec-experts
mailing list