Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java lambda - for loop counter is not effectively final

Given this situation where a lambda is inside a for loop I would expect the counter i to be effectively final.

The compiler complains that i is not effectively final so I had to use i2.

for (int i = 0; i < x.getBooks().size(); i++){
   //The compiler needs i to be effectively final.
   int i2 = i;
   List<Book> books = bookstore.stream()
                    .filter(c -> c.getAuthors().get(i2).equals("xxx"))
                    .collect(Collectors.toList());
}

So the question is why i is not effectively final inside the scope of the for loop and is this the simplest workaround.

like image 985
Dan Avatar asked Jul 22 '14 17:07

Dan


2 Answers

As the other answers mention, effectively final means, that a variable only can be assigned once and will not be reassigned. That's not the case in a for-loop. effectively final exists, because otherwise developers have to explicitly mark a variable as final to use it in a lambda.

However, the reason i'm answering is a solution, to write your code without duplicating i:

IntStream.range (0, x.getBooks().size()).forEach (i -> {
    List<Book> books = bookstore.stream()
                                .filter(c -> c.getAuthors().get(i).equals("xxx"))
                                .collect(Collectors.toList());
});
like image 185
F. Böller Avatar answered Sep 19 '22 00:09

F. Böller


TLDR: i is not final because it is modified (i++) at each iteration of the for loop.


why i is not effectively final inside the scope of the for loop ?

The for loop syntax is

for (initialization; termination; increment) {
    statement(s)
}

The increment expression is invoked after each iteration through the loop. In your case, increment is i++ so i is modified after each iteration.

You could confirm this by declaring i final:

for (final int i = 0; i < x.getBooks().size(); i++) {
}

you will get this compilation error:

The final local variable i cannot be assigned.
It must be blank and not using a compound  assignment

is this the simplest workaround ?

In the case of a for loop: yes.

But you could use a while loop as shown by @dkatzel or a foreach:

int i = 0;
for (Book book: x.getBooks()) {
    int i2 = i;
    ...  
    i++;
}
like image 27
gontard Avatar answered Sep 19 '22 00:09

gontard