Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Local variable log defined in an enclosing scope must be final or effectively final

I'm new to lambda and Java8. I'm facing following error.

Local variable log defined in an enclosing scope must be final or effectively final

public JavaRDD<String> modify(JavaRDD<String> filteredRdd) {      filteredRdd.map(log -> {          placeHolder.forEach(text -> {              //error comes here             log = log.replace(text, ",");          });          return log;      });      return null; } 
like image 978
Balaji Reddy Avatar asked Jul 15 '16 18:07

Balaji Reddy


People also ask

How resolve variable used in lambda expression should be final or effectively final?

The local variables that a lambda expression may use are referred to as “effectively final”. An effectively final variable is one whose value doesn't change after it's first assigned. There is no need to explicitly declare such a variable as final, although doing so would not be an error.

Why local variables referenced from a lambda expression must be final or effectively final?

Forcing the variable to be final avoids giving the impression that incrementing start inside the lambda could actually modify the start method parameter.

What is enclosing scope in Java?

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.

What is effectively final in Java?

In simple terms, objects or primitive values are effectively final if we do not change their values after initialization. In the case of objects, if we do not change the reference of an object, then it is effectively final — even if a change occurs in the state of the referenced object.


2 Answers

The message says exactly what the problem is: your variable log must be final (that is: carry the keyword final) or be effectively final (that is: you only assign a value to it once outside of the lambda). Otherwise, you can't use that variable within your lambda statement.

But of course, that conflicts with your usage of log. The point is: you can't write to something external from within the lambda ... so you have to step back and look for other ways for whatever you intend to do.

In that sense: just believe the compiler.

Beyond that, there is one core point to understand: you can not use a local variable that you can write to. Local variables are "copied" into the context of the lambda at runtime, and in order to achieve deterministic behavior, they can only be read, and should be constants.

If your use case is to write to some object, then it should be a field of your enclosing class for example!

So, long story short:

  • local variables used (read) inside a lambda must act like a constant
  • you can not write to local variables!
  • or the other way round: if you need something to write to, you have to use a field of your surrounding class for example (or provide a call back method)
like image 174
GhostCat Avatar answered Sep 19 '22 23:09

GhostCat


The reason for this limitation is the same as the reason for the Java language feature that local variables accessed from within (anonymous) inner classes must be (effectively) final.

This answer by rgettman gets into the details of it. rgettman explains the limitations in clear detail and I link to that answer because the behavior of lambda expressions should be same as that of anonymous inner classes. Note that such limitation does not exist for class or instance variables, however. The main reason for this is slightly complicated and I couldn't explain it better than what Roedy Green does it here. Copying here only so it is at one place:

The rule is anonymous inner classes may only access final local variables of the enclosing method. Why? Because the inner class’s methods may be invoked later, long after the method that spawned it has terminated, e.g. by an AWT (Advanced Windowing Toolkit) event. The local variables are long gone. The anonymous class then must work with flash frozen copies of just the ones it needs squirreled away covertly by the compiler in the anonymous inner class object. You might ask, why do the local variables have to be final? Could not the compiler just as well take a copy of non-final local variables, much the way it does for a non-final parameters? If it did so, you would have two copies of the variable. Each could change independently, much like caller and callee’s copy of a parameter, however you would use the same syntax to access either copy. This would be confusing. So Sun insisted the local be final. This makes irrelevant that there are actually two copies of it.

The ability for an anonymous class to access the caller’s final local variables is really just syntactic sugar for automatically passing in some local variables as extra constructor parameters. The whole thing smells to me of diluted eau de kludge.

like image 22
Kedar Mhaswade Avatar answered Sep 17 '22 23:09

Kedar Mhaswade