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.
TLDR:
i
is notfinal
because it is modified (i++
) at each iteration of thefor
loop.The
for
loop syntax isThe increment expression is invoked after each iteration through the loop. In your case, increment is
i++
soi
is modified after each iteration.You could confirm this by declaring
i
final:you will get this compilation error:
In the case of a
for
loop: yes.But you could use a
while
loop as shown by @dkatzel or aforeach
:From the Java Language Specification
And about effectively final
Your
i
variable occurs as the operand of postfix increment operatorIt is therefore not effectively final and can not be captured by the lambda.
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 asfinal
to use it in a lambda.However, the reason i'm answering is a solution, to write your code without duplicating
i
:You have to remember that a
for
loop actually equivalent to :So you see that
i
is declared only once and updated therefore not effectively final.It's a little unclear the relationship between
x.getBooks()
andBook.getAuthors()
but apparently they have the same size? Without a better understanding, I don't think I can show you a better way to do it. But this might also show you that your design is poor.