1

I just saw this question, and apparently it's obvious that Java should deny access to non-final variables inside the body of a lambda expression. Why?

Edit: for example, I don't see why the following code is harmful:

String[] numbers = new String[10]; // put some numerical strings in
BigInteger sum = new BigInteger("0");
numbers.forEach(n -> sum = sum.add(new BigInteger(n)));
Community
  • 1
  • 1
Bluefire
  • 13,519
  • 24
  • 74
  • 118
  • I think its easier to understand if you state "local non-final variables". E.g. the variables declared inside the methods. – n247s Mar 02 '17 at 19:17
  • @n247s Inside the methods? As in inside the lambda function, or the method that contains it? – Bluefire Mar 02 '17 at 19:17
  • 2
    Because the lambda may last longer than the block that encapsulates it. – Jerfov2 Mar 02 '17 at 19:18
  • @Jerfov2 Is the lambda asynchronous? – Bluefire Mar 02 '17 at 19:19
  • @Bluefire Sorry, I don't understand your question – Jerfov2 Mar 02 '17 at 19:24
  • You might look at this answer: http://stackoverflow.com/questions/42552126/java-8-stream-expression-to-or-several-enum-values-together/42559245#42559245 which explains that the value used within the lambda may be a copy, so updating it would be fruitless. local variables referenced in a lambda may be final, but if not, must be "effectively final". – Hank D Mar 02 '17 at 19:37
  • @Jerfov2 "the lambda may last longer than the block that encapsulates it" I could imagine that but only if the lambda function was asynchronous to the method, i.e. the method does not wait for the lambda to finish to carry on with execution. – Bluefire Mar 02 '17 at 19:37
  • @Bluefire that would depend on the method using the lambda expression. For example Java's `ExecutorService` can call the lambda expressions concurrently and synchronously. The problem, I believe, is not the scope though. Using a non-final variable may simply cause a race condition when the expression is run concurrently, and the compiler warns you before this could even happen. You can have a look at the link on my answer. – halileohalilei Mar 02 '17 at 21:04
  • There is no `forEach` method on a `String[]` array. Besides that, I don’t understand, why you don’t follow the links in the other question and read the explanations if you already know it. – Holger Mar 03 '17 at 19:31

2 Answers2

5

Lambdas are just syntactic sugar and they get compiled into anonymous inner classes. Anonymous inner classes can't use non-final local variables because of their scope. Here's the explanation:

The local variables of the method live on the stack, and exist only for the lifetime of the method. You already know that the scope of a local variable is limited to the method the variable is declared in. When the method ends, the stack frame is blown away and the variable is history. But even after the method completes, the inner class object created within it might still be alive on the heap if, for example, a reference to it was passed into some other code and then stored in an instance variable. Because the local variables aren't guaranteed to be alive as long as the method-local inner class object, the inner class object can't use them. Unless the local variables are marked final!

Courtesy: SCJP 6 Study guide by Kathy Sierra and Bert Bates

Darshan Mehta
  • 30,102
  • 11
  • 68
  • 102
  • 1
    This explanation is lacking. Why are finals okay then? They would still disappear when the stack frame is destroyed wouldn't they? – puhlen Mar 02 '17 at 19:26
  • This is an explanation for anonymous inner classes. The rules are relaxed for lambdas – Thorbjørn Ravn Andersen Mar 02 '17 at 19:53
  • 2
    @puhlen: Because the lambda/anonymous class instance/local class instance can outlive the function, the lambda/anonymous class instance/local class instance gets a copy of the variable when the the lambda/anonymous class instance/local class instance is created, and the lambda/anonymous class instance/local class instance will use its own copy of the variable. So now there are two independent copies of the variable. If it were non-final, changes to one copy wouldn't be reflected in another copy of the same variable. To prevent that, it is required to be final (or effectively final, in Java 8). – newacct Mar 04 '17 at 03:15
0

I'm not sure if this is the answer, but in Java's documentation, it states the following:

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.

What I understand from this is that when you pass a lambda expression to a method, the expression should not cause concurrency problems, since this method can be used concurrently by multiple threads, and using non-final variables would likely cause racing conditions.

The documentation page if you want to read it for yourself and get further info: http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.27.2

halileohalilei
  • 2,220
  • 2
  • 25
  • 52