I have 2 code samples:
int[] idx = { 0 };
List<String> list = new ArrayList<String>();
list.add("abc");
list.add("def");
list.add("ghi");
list.stream().forEach(item -> {
System.out.println(idx[0] + ": " + item);
idx[0]++;
});
Working properly.
While this code has compile error:
int idx = 0;
List<String> list = new ArrayList<String>();
list.add("abc");
list.add("def");
list.add("ghi");
list.stream().forEach(item -> {
System.out.println(idx + ": " + item);
idx++;
});
Saying:
Local variable idx defined in an enclosing scope must be final or effectively final.
The only difference is idx
int or int array.
What's the root cause?
The root cause is that JVM lacks mechanisms of constructing references to local variables, which is what is needed to perform
idx++
whenidx
is anint
or some immutable type (e.g.String
). Since you try to mutateidx
, simply capturing its value would not be sufficient; Java would need to capture a reference, and then modify the value through it.Java does not have this problem when you use an array, because arrays are reference objects. Java can capture array reference that never changes, and use that non-changing reference to mutate the object. Array itself provides the necessary level of indirection, because Java arrays are mutable.
Because in this situation there is no need for the lambda to capture a reference to a local variable of primitive type. A reference to the static variable is readily available, so there is no problem with capturing it.
Similarly, the code would work if you make
idx
a member variable, and use your lambda inside an instance method. This would let lambda modifyidx
field throughthis
object, which could be freely captured.I have a partial explanation for your observations. The initialized array in your Java 8 code is considered as effectively final, because its value does not change after initialization. This is why the
int[] idx = { 0 };
version of your code is getting through. So I would expect if you makeint idx
effectively final, then it would also pass. One way to do this would be to formally make this variable final by declaring it so, i.e.final int idx = 0
.A variable or parameter whose value is never changed after it is initialized is effectively final.
In case 1:
int[] idx
don't change, if you replaceidx[0]++;
toidx = {1};
will compile errorIn case 2:
if you remove
idx++
; it will compile fine