Calling a non-final lambda from another lambda in Java 8

by 1/09/2014 12:05:00 PM 0 comments
I am playing with Java 8's lambdas. In the past, Java has required that parameters 'closed over' by inner classes need to be final. With Java 8's lambdas, closed over parameters no longer need to be explicitly declared final, but they still need to be effectively final. Here's an example:



import java.util.function.*;

class LambdaTest {
  public static void main(String[] args) {
    Consumer fn = (Void) -> { System.out.println("One"); };
    Runnable r = () -> { fn.accept(null); };
    r.run();
  }
}


fn is closed over by the Runnable that we created and runs as expected producing the output of "One". However, if we try to change the value of fn the compiler will complain:



import java.util.function.*;

class LambdaTest2 {
  public static void main(String[] args) {
    Consumer fn = (Void) -> { System.out.println("One"); };
    Runnable r = () -> { fn.accept(null); };
    r.run();
    fn = (Void) -> { System.out.println("Two"); }; // compiler barfs
    r.run();
  }
}


Since we've changed the value of fn the compiler will respond with: error: local variables referenced from a lambda expression must be final or effectively final. The workaround for this is to indirectly call fn via a method:



import java.util.function.*;

class LambdaTest3 {
  
  private static Consumer fn;
  
  public static void main(String[] args) {
    fn = (Void) -> { System.out.println("One");};
    // Do not call fn directly, use a method call instead.
    Runnable r = () -> {callFn();};
    r.run();
    fn = (Void) -> {System.out.println("Two");};
    r.run();
  }
  
  private static void callFn() {
    fn.accept(null);
  }
 
}


Now we get the output that we were expecting; the above programs will print "One" then "Two".

Brian Schlining

Developer

Cras justo odio, dapibus ac facilisis in, egestas eget quam. Curabitur blandit tempus porttitor. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.

0 comments: