Why restrict local variables to be definitive in a Java lambda expression?


This question already has an answer here:

  • Lambdas: local variables need final, instance variables don't 7 answers


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.

Captured variables work by copying their values. For example, suppose there is some anonymous inner class like this:

String message = "hello world!";
new Thread(new Runnable() {
    public void run() {

This will compile to code which is actually similar to the following:

class AnonymousRunnable0 implements Runnable {
    final String messageCopy;
    AnonymousRunnable0(String message) {
        this.messageCopy = message;
    public void run() {

String message = "hello world!";
new Thread(new AnonymousRunnable0(message)).start();

Capturing lambdas work in a similar fashion.

So the point is that when you capture variables, you do not actually have access to the outer scope as if with reference semantics, you just have copies of the values.

Also, if what you're asking for was possible, passing references to local variables around just makes for really wacky code. What if the lambda which captured the local variable gets put in a List somewhere and the method returns? Now you have a reference to a variable which doesn't exist anymore. This is the sort of thing Java is designed away from.