I can't understand why the implementers of the java language made it so that the variable used in a lambda and passed there from a function scope has to be final.
I decompiled this code:
public class Main {
@FunctionalInterface
interface Test {
void method(int t);
}
static void test(Test t) {
t.method(3);
}
public static void main(String... args) {
int a = 3;
test((i)-> {
System.out.println(a + i);
});
}
}
and what the compiler does is copy that variable as if it was passed through a constructor. I got these 3 classes:
1:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import Main.1;
import Main.Test;
public class Main {
public Main() {
}
static void test(Test var0) {
var0.method(3);
}
public static void main(String... var0) {
byte var1 = 3;
test(new 1(var1));
}
}
2:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import Main.Test;
final class Main$1 implements Test {
Main$1(int var1) {
this.val$a = var1;
}
public void method(int var1) {
System.out.println(this.val$a + var1);
}
}
3:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
@FunctionalInterface
interface Main$Test {
void method(int var1);
}
Why couldn't the implementers just copy the variable regardless of whether it was modified or not so we could do this:
public class Main {
@FunctionalInterface
interface Test {
void method(int t);
}
static void test(Test t) {
t.method(3);
}
public static void main(String... args) {
int a = 3;
test((i)-> {
a += 1; // allow modification, a is copied anyway, why not?
System.out.println(a + i);
});
}
}
There's no technical reason for this. It's just that if you allow non-final fields to be used in a lambda then you could write code that looks fine, but actually doesn't work.
For example:
void printSum(Collection<Integer> numbers) {
int sum = 0;
numbers.forEach(i -> sum += i);
System.out.println(sum);
}
Currently the compiler won't let you do that, since you can't access the non-final sum
inside the lambda. As you noted the variable gets copied into the lambda anyway, so it could allow that.
If it did, then this code would compile, but always print 0, since only the sum
-copy inside the lambda is modified and not the "real one".
Allowing only "effectively final" variables to be referenced is a good compromise of not requiring the final
keyword everywhere while still avoiding misunderstandings like that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With