Jack
Jack

Reputation: 77

Java8 Stream compiler message -- local variable must be final or effectively final

I have a little problem. When I write this for loop, the variable i in f.getAnswerScore().get(i).... is underlined with error message : - Local variable i defined in an enclosing scope must be final or effectively final. Does this have something to do with streams? Maybe streams can't be used in loops?

for (int i = 0; i < 10; i++) {
    correct = active.stream()
        .filter(f -> f.getAnswerScore().get(i).getStatus().equals(AnswerStatus.ANSWERED_CORRECT))
        .count();
}

Upvotes: 3

Views: 8065

Answers (2)

Matthias Berndt
Matthias Berndt

Reputation: 4587

rgettman's answer is a workaround. You never use correct inside the loop, so why bother assigning to it 10 times rather than running it just once with i = 9?

The answer is that you're probably doing something else with correct that you're not showing us, and a thorough answer to the question would probably require us to know what that is. Because it's quite unlikely that a C-style for loop like you've written is actually the best/most elegant solution to the problem you're trying to solve.

One possibility is to collect all the correct values in another stream:

IntStream.range(0, 10).map(i ->
  active
  .stream()
  .filter(f ->
     f.getAnswerScore()
      .get(i)
      .getStatus()
      .equals(AnswerStatus.ANSWERED_CORRECT)
  )
  .count()
)

Of course, you might want to do something else entirely, there's no way for us to know.

Upvotes: 3

rgettman
rgettman

Reputation: 178263

Like anonymous inner classes, lambda expressions can only access local variables if they are final or "effectively final" (Java 8 or higher; not final but never changed once assigned).

This is covered by the JLS, Section 15.27.2:

Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression must either be declared final or be effectively final (§4.12.4), or a compile-time error occurs where the use is attempted.

Any local variable used but not declared in a lambda body must be definitely assigned (§16 (Definite Assignment)) before the lambda body, or a compile-time error occurs.

Similar rules on variable use apply in the body of an inner class (§8.1.3). The restriction to effectively final variables prohibits access to dynamically-changing local variables, whose capture would likely introduce concurrency problems. Compared to the final restriction, it reduces the clerical burden on programmers.

Declare a final variable equal to i and use it.

for(int i = 0; i< 10; i++){
     final int j = i;
     correct = active
         .stream()
         .filter(f-> f.getAnswerScore().get(j).getStatus().equals(AnswerStatus.ANSWERED_CORRECT))
         .count();
}

Upvotes: 7

Related Questions