6

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.

5
  • where are you using i or i2 ? Commented Jul 22, 2014 at 17:33
  • effectively final? I'm not sure what that means. Your code doesn't even use i in the lambda Commented Jul 22, 2014 at 17:37
  • apologies for the typo - edited now. Commented Jul 22, 2014 at 18:00
  • 3
    This is what effectively final means stackoverflow.com/questions/20938095/… Commented Jul 22, 2014 at 18:01
  • IMO they should allow final int i which would then prevent modification of i outside the for construct. Commented Aug 9, 2021 at 16:45

4 Answers 4

6

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());
});
Sign up to request clarification or add additional context in comments.

Comments

6

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++;
}

1 Comment

In your foreach example the semantics of i have been changed. It was 0 the first time around the loop. It will now be 1 the first time around. Also, the same problem with it not being effectively final exists, so i2 would still be needed.
2

From the Java Language Specification

The scope of a local variable declared in the ForInit part of a basic for statement (§14.14.1) includes all of the following:

  • Its own initializer
  • Any further declarators to the right in the ForInit part of the for statement
  • The Expression and ForUpdate parts of the for statement
  • The contained Statement

And about effectively final

A local variable or a method, constructor, lambda, or exception parameter is effectively final if it is not declared final but it never occurs as the left hand operand of an assignment operator (§15.26) or as the operand of a prefix or postfix increment or decrement operator

Your i variable occurs as the operand of postfix increment operator

i++

It is therefore not effectively final and can not be captured by the lambda.

Comments

0

You have to remember that a for loop actually equivalent to :

int i=0;
while(i < x.getBooks().size()){
  //execute your block
  i++;
}

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() and Book.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.

2 Comments

The actual example is somewhat manufactured so don't worry about that too much. The main question pertains to the scope of the counter in the for loop.
@Dan OK, then maybe the first part of my answer is sufficient?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.