Java 8 Lambda Expressions Capture Values, Not Variables

Before Java 8, when we needed to use a local variable inside an anonymous inner class, the local variable had to be declared final:

public class ValueCaptureAnon {
  public static void main(String[] args) {
    String name = "Yagiz"; //Compile error in Java 7...

    Runnable runnable = new Runnable() {
      @Override
      public void run() {
        System.out.println("Name is " + name);
      }
    };
    
    runnable.run();
  }
}

The above code doesn’t compile in Java 7 because the variable name is not declared final:

local variable name is accessed from within inner class; needs to be declared final

However in Java 8 you don’t have this restriction anymore… Uhmm, kinda… It is now possible to refer to the variables that are NOT final but they still need to be effectively final. And this applies to both anonymous inner classes and lambdas expressions:

Anonymous Inner Class

When we switch to Java 8 the code shown above (ValueCaptureAnon.java) compiles and runs fine because the compiler detects that name remains unchanged (effectively final). If I modify the code and assign a new value to name the compiler complains about it:

    String name = "Yagiz";

    name = name + " Erkan"; //Compile error in Java 8.

    Runnable runnable = new Runnable() {
      @Override
      public void run() {
        System.out.println("Name is " + name);
      }
    };

    name = name + " Erkan"; //Compile error in Java 8.

    runnable.run();

As we can see, the compiler raises an error about the assignment, even when this is done before the anonymous inner class declaration.

Lambda Expression

Unsurprisingly we also observe the same behaviour with the new Lambda Expressions.
The following code runs fine without declaring name final.

public class ValueCapture {
  public static void main(String[] args) {
    String name = "Yagiz";

    Runnable runner = () -> System.out.println("Name is: " + name);
    runner.run();
  }
}

However as soon as we introduce an assignment the compiler stops us:

public class ValueCapture {
  public static void main(String[] args) {
    String name = "Yagiz";

    Runnable runner = () -> System.out.println("Name is: " + name);
    //name = "Erkan"; //Compile error: local variables referenced from a lambda expression
                      // must be final or effectively final
    runner.run();
  }
}

Source Code

You can download the source code form my GitHub repository.

, , , ,

  1. #1 by Anthony Geoghegan on April 26, 2014 - 8:47 am

    That’s typical behaviour for a closure though right?

  2. #2 by Anthony Geoghegan on April 26, 2014 - 9:07 am

    Are these Java lambda’s evaluated immediately? Because that would explain the behaviour. I’m just wondering if these lambdas are true functions though. In other words can they have deferred execution. In JavaScript the reference to the local value is evaluated as the value it has on exiting the containing function, I think.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: