Java lambda - for loop counter is not effectively final

后端 未结 4 1891
感情败类
感情败类 2021-01-19 00:18

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

相关标签:
4条回答
  • 2021-01-19 00:45

    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());
    });
    
    0 讨论(0)
  • 2021-01-19 00:45

    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.

    0 讨论(0)
  • 2021-01-19 00:47

    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.

    0 讨论(0)
  • 2021-01-19 00:50

    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++;
    }
    
    0 讨论(0)
提交回复
热议问题