Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

java

lambda

final

  • 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.

like image 820
huangjs Avatar asked Oct 23 '19 07:10

huangjs


People also ask

Why did we have to make some variables final or effectively final when using them in an anonymous class?

Forcing the variable to be final avoids giving the impression that incrementing start inside the lambda could actually modify the start method parameter.

Why do local variables used in lambdas have to be final or effectively final?

The local variables that a lambda expression may use are referred to as “effectively final”. An effectively final variable is one whose value doesn't change after it's first assigned. There is no need to explicitly declare such a variable as final, although doing so would not be an error.

When we refer any variable as effectively final which key is used?

4 states that if we remove the final modifier from a method parameter or a local variable without introducing compile-time errors, then it's effectively final. Moreover, if we add the final keyword to a variable's declaration in a valid program, then it's effectively final.

Why are final variables used?

Final variables can be used to construct trees of immutable objects. Once constructed, these objects are guaranteed not to change anymore. To achieve this, an immutable class must only have final fields, and these final fields may only have immutable types themselves.


1 Answers

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.

like image 186
Zabuzard Avatar answered Oct 05 '22 11:10

Zabuzard