Java - changing final variable value from lambda

In Java, I have the following code

List<Integer> myList = new ArrayList<>(); for (int i=0;i<9;i++) { myList.add(i); } Integer sum = 0; myList.forEach(i -> { sum = sum + i; // does not compile, sum needs to be final or effectively final }); for(int i : myList) { sum = sum + i; //runs without problems } 

My question is: why can't I change the value of the sum from lambda? Does it do the same as for the cycle below, or am I wrong? It is also interesting that if I declare an Integer sum outside the main method as static, it also works. Can someone explain to me why?

EDIT: in another similar question, Does Java 8 support closure , the answer is as follows:

it is a combination of backward compatibility and project resource limits.

However, I still can not understand why it works if I make the sum an array or declare it outside the main one. I would also like to understand the difference between myList.forEach and the for loop below, why one works and the other doesn't.

+4
source share
3 answers

Try:

 final Integer[] sum = new Integer[1]; sum[0] = 0; myList.forEach(i -> { sum[0] = sum[0] + i; // does not compile, sum needs to be final or effectively final }); 

Because lambda is actually syntactic sugar for initializing an anonymous class (and overriding the method).

This is the same as if you wrote:

 final Integer[] sum = new Integer[1]; sum[0] = 0; myList.forEach(new Consumer() { public void accept(Integer element) { sum[0] = sum[0] + element; } }); 

The variable that comes from the outer region and which is used in the inner region must be final (in this example, sum ). This is simply because Java does not support closure. Therefore, the external variable must be marked as final. Since Integer itself is immutable (if you declare it final, you can no longer modify it), you should use a wrapper object or an array (like me).

Here you can find more useful information:

+3
source

Not the answer you're looking for, but in most scenarios you don't need to change this inside the lambda. This is due to the fact that for lambdas it’s not so idiomatic to change in the right functional style.

What you can do to achieve your result, use any functions of a higher level to mask the "battery", and then assign:

 sum = myList.stream().mapToInt(x->x).sum(); 
+2
source

Lambda is basically an anonymous class. You can only access final local variables from an anonymous class.

What you need is a wrapper class that can modify its contents. For a quick hack, you can use AtomicInteger in this case:

 AtomicLong sum = new AtomicLong(0); myList.forEach(i -> { sum.addAndGet(i); // does not matter getAndAdd or addAndGet }); sum.get(); // to get the value 

Another way: if you use Intellij IDEA, the IDE may offer you to convert the variable to the last array of elements (as in darijan's answer).

0
source

All Articles