Why is the loop variable effectively final when using for-each?

huangjs :
  • case 1: it can work when using for-each loop:
    private void m10(String[] arr) {
        for (String s : arr) {
            Supplier<String> supplier = () -> {
                System.out.println(s);
                return null;
            };
            supplier.get();
        }
    }

or

    private void m10(Object[] arr) {
        for (Object s : arr) {
            Supplier<String> supplier = () -> {
                System.out.println(s);
                return null;
            };
            supplier.get();
        }
    }
  • case 2: it will catch compile-time error
    private void m11(String[] arr) {
        for (int i = 0; i < arr.length; i++) {
            Supplier<String> supplier = () -> {
                System.out.println(arr[i]);
                return null;
            };
            supplier.get();
        }
    }

In case 2, I know the variable i is not effectively final because its value changed between loop iterations. But I cannot understand why the lambda can work in case 1.

Zabuza :

s is never changed (s = ...). So the compiler says "yeah, we could theoretically mark this as final". That is what is meant by effectively final. I.e. you did not mark it final but you could and it would still compile.

In case you are wondering about the enhanced for-loop:

for (String s : arr)

The variable does not live outside of the scope of the for and does not get re-assigned. I.e. it is not:

String s = null;
for (int i = 0; i < arr.length; i++) {
    s = arr[i];
    ...
}

The variable is created inside the loop, so its scope is limited to the loop. It is not re-used but thrown away and re-created each iteration:

for (int i = 0; i < arr.length; i++) {
    String s = arr[i];
    ...
}

Take a close look at the two examples. In the first, you could not write final String s = null;, because we are re-assigning it during the loop s = arr[i];. But in the second example we can, because the variable s is only known within one iteration and then thrown away again. So final String s = arr[i]; is fine.

As a side note, this also explains why you can not use s after the loop. It is unknown and destroyed already, its scope is limited to the loop.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=321933&siteId=1