Code:
int counter = 0;
int[] counterInArray = {1};
IntStream.range(1, 100)//couldn't compile since counter is not effectively final
.forEach(x -> System.out.println(x + ": " + counter++));
IntStream.range(1, 100)//Works well
.forEach(x -> System.out.println(x + ": " + counterInArray[0]++));
IntStream.range(1, 100).parallel()//parallel breaks the counter, so we should be careful
.forEach(x -> System.out.println(x + ": " + counterInArray[0]++));
As you see, we can do a simple hack(put it into an array) to make a variable effectively final and works well in single thread situation.
So why restrict variable to be effectively final in a Java lambda expression?
We have to hack it when we want to use some variable which is not effectively final but it works well with single thread stream.
You may read my post to see how hard to find a good way to hack down a counter(which is not effective final, of course) in lambda.