This post talks about how to resolve "local variable defined in an enclosing scope must be final or effectively final" error while trying to write a lambda expression in Java.
Let's first get some background on what is effectively final; that will help you to get an idea why this error is coming.
Effectively Final in Java
When a lambda expression uses an assigned local variable from its enclosing space there is an important restriction.
A lambda expression may only use local variable whose value doesn't change. That restriction
is referred as "variable capture" which is described as; lambda expression capture values, not variables.
The local variables that a lambda expression may use are known as "effectively final".
An effectively final variable is one whose value does not change after it is first assigned. There is no need to explicitly declare such a variable as final, although doing so would not be an error. Since there is no need to explicitly declare such a variable as final thus the name effectively final. If there is an attempt to change such a variable, anyway compiler will throw an error.
Let's see it with an example. Here I have a functional interface IFunc which has a single abstract method display. Since it has a single abstract method it is a functional interface and lambda expression can be used to implement this functional interface. Just to make it clear I have also used the @Functional interface annotation.
In the code lambda expression that implements the display method of the interface just prints the value of the local variable on the console. It can be noted that there is no need to declare variable i as final which was a requirement before Java 8 (in case of anonymous class).
@FunctionalInterface interface IFunc{ void display(); } public class InnerDemo { public static void main(String[] args) { int i = 7; // lambda expression that implements the display method // of the IFunc functional interface IFunc ifunc = ()-> System.out.println("Value of i is " + i); // Calling the display method ifunc.display(); } }
Output
Value of i is 7
It can be seen if we are not changing the value of local variable i, it can be used with lambda expression and there is no need to declare i as final.
When will it give error
As already pointed out while discussing effectively final "A lambda expression may only use local variable whose value doesn't change". So if you try to change the value of i with in the lambda expression you'll get the error "Local variable i defined in an enclosing scope must be final or effectively final".
Code that gives compiler error
@FunctionalInterface interface IFunc{ void display(); } public class InnerDemo { public static void main(String[] args) { int i = 7; // lambda expression that implements the display method // of the IFunc functional interface IFunc ifunc = ()-> System.out.println("Value of i is " + i++); // Calling the display method ifunc.display(); } }
Here I have changed the i to i++ in System.out thus the program gives compile time error "Local variable i defined in an enclosing scope must be final or effectively final".
So it should be clear by now what it means for a variable to be effectively final and why do you get this error "local variable defined in an enclosing scope must be final or effectively final".
Solution to get around this error
Since it is a rule in Java programming language that variable in an enclosing scope can't be change in inner class or lambda expression, so you can't change the value of the variable. That said, there is a get around which I have used and that get around is to use array.
If we take the previous example again by using an int[] array instead of int variable-
@FunctionalInterface interface IFunc{ void display(); } public class InnerDemo { public static void main(String[] args) { int[] numArr = {7}; // lambda expression that implements the display method // of the IFunc functional interface IFunc ifunc = ()-> System.out.println("Value of i is " + (numArr[0]+1)); // Calling the display method ifunc.display(); } }
Output
Value of i is 8
As you can see it works now and "local variable defined in an enclosing scope must be final or effectively final" error is not thrown anymore.
Works very well with boolean flags and that's where I have used it. Let's see a small example.
public class Test{ public static void main(String[] args) { Listapplications = Arrays.asList("A", "B"); List user = Arrays.asList("A"); Boolean[] arr = {true}; applications.forEach( a -> { for (String str : user) { if(a.equals(str)) { //error resolved: Local variable flag defined in an enclosing scope must be final or effectively final arr[0] = false; break; }else{ arr[0] = true; } } if(!arr[0]) { System.out.println("Here with false"); }else{ System.out.println("Here with true"); } }); } }
Output
Here with false Here with true
That's all for this topic How to Resolve Local Variable Defined in an Enclosing Scope Must be Final or Effectively Final Error. If you have any doubt or any suggestions to make please drop a comment. Thanks!
Related Topics
You may also like-
The post clearly mentions that you are suggesting how to resolve this issue. If we need to change the variable's value, how are we supposed to do that?
ReplyDeleteYou can't change the variables value. I think the author meant to explain the reason for getting this error, so that you can understand it and avoid it.
DeleteNow as to why you can't change the value is as follows:
Local variables in Java have until now been immune to race conditions and visibility problems because they are accessible only to the thread executing the method in which they are declared. But a lambda can be passed from the thread that created it to a different thread, and that immunity would therefore be lost if the lambda, evaluated by the second thread, were given the ability to mutate local variables.
Refer this link for detailed explanation: http://www.lambdafaq.org/what-are-the-reasons-for-the-restriction-to-effective-immutability/
In the comments section of https://netjs.blogspot.com/2015/06/lambda-expression-and-variable-scope.html I have given one way to get round this error, please have a look.
DeleteThe post title says that it resolves the issue, but doesn't. All it does is explain the issue. Change the post title so people don't get confused.
ReplyDeleteI beg to differ--it worked for me.
DeleteJust use AtomicReference and make it final.
ReplyDelete