Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Lambda Referencing Enclosing Object: Replace with Private Static Class?

A Java lambda referencing an element from its enclosing scope holds a reference to its enclosing object. A contrived example, with lambda holding ref to MyClass:

class MyClass {
  final String foo = "foo";
  public Consumer<String> getFn() {
    return bar -> System.out.println(bar + foo);
  }
}

This is problematic if the lifetime of the lambda is long; then we've got a ref to MyClass that is long-lived, when it would have otherwise gone out of scope. Here we can optimize by replacing the lambda with a private static class, so that we're only holding a reference to the String we need rather than to the entire class:

class MyClass {

  private static class PrintConsumer implements Consumer<String> {

    String foo;

    PrintConsumer(String foo) {
      this.foo = foo;
    }

    @Override
    public void accept(String bar) {
      System.out.println(bar + foo);
    }
  }

  final String foo = "foo";

  public Consumer<String> getFn() {
    return new PrintConsumer(foo);
  }
}

Unfortunately this is super verbose and destroys the nice syntax we get from using (effectively final) variables from enclosing scope in lambdas. Is this technically optimal? Is there always a tradeoff here between nice syntax and the possibility of keeping a ref longer than necessary?

like image 304
T_T Avatar asked Mar 23 '16 19:03

T_T


People also ask

How to hide variables from the enclosing scope of a Lambda?

We can’t hide variables from the enclosing scope inside the lambda’s body. In this case, the keyword this is a reference to an enclosing instance. For example, in the class UseFoo, we have an instance variable value: Then in some method of this class, place the following code and execute this method:

How to use static method in lambda expression?

Static Method References. A static method reference allows us to use a static method as a lambda expression. The static methods can be defined in a class, an interface, or an enum. The following code defines two lambda expressions. The first lambda expression func1 is created by defining an input parameter x and providing lambda expression body ...

What is Lambda in Java with example?

Java Lambda - Java Static Method Reference. A lambda expression represents an anonymous function defined in a functional interface. A method reference creates a lambda expression using an existing method. Two consecutive colons act as a separator.

What is a static method reference in Java?

A static method reference allows us to use a static method as a lambda expression. The static methods can be defined in a class, an interface, or an enum.


2 Answers

Assign your member to a local variable first:

class MyClass {
  final String foo = "foo";
  private Consumer<String> getFn() {
    String localFoo = foo;
    return bar -> System.out.println(bar + localFoo);
  }
}

Now, the lambda only captures local variables inside of getFn(). MyClass.this is no longer captured.

Another option, slightly more verbose, delegate to a helper method:

class MyClass {
  final String foo = "foo";
  private Consumer<String> getFn() {
    return getFn(foo);
  }
  private static Consumer<String> getFn(String localFoo) {
    return bar -> System.out.println(bar + localFoo);
  }
}
like image 135
Lukas Eder Avatar answered Sep 21 '22 12:09

Lukas Eder


A combination of Lukas Eder's local-final-variable and helper-method-delegation solutions:

class MyClass {
  final String foo = "foo";
  private Consumer<String> getFn() {
    return apply(
      foo,
      localFoo -> bar -> System.out.println(bar + localFoo)
    );
  }
  private static <IN, OUT> OUT apply(
    final IN in,
    final Function<IN, OUT> function
  ) {
    return function.apply(in);
  }
}
like image 37
srborlongan Avatar answered Sep 21 '22 12:09

srborlongan