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

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)

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.


Remember method inner classes can`t modify any value from their surrounding method. Your second lambda expression in forecach is trying to access its surrounding method variable (log).

To solve this you can avoid using lambda in for each and so a simple for each and re-palace all the values in log.

        filteredRdd.map(log -> {
        for (String text:placeHolder){
            log = log.replace(text,",");
        }
        return log;
    });