Java Lambda 引用封闭对象:替换为私有静态类?

从其封闭作用域引用元素的 Java lambda 包含对其封闭对象的引用。一个人为的例子,lambda持有MyClass的引用:

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

如果 lambda 的生存期很长,这是有问题的;然后我们有一个MyClass的参考,它是长期存在的,否则它就会超出范围。在这里,我们可以通过将lambda替换为私有静态类进行优化,以便我们只保留对所需字符串的引用,而不是对整个类的引用:

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);
  }
}

不幸的是,这是非常冗长的,并且破坏了我们从使用(实际上是最终的)变量中获得的漂亮语法,这些变量来自lambdas中的封闭范围。这在技术上是最佳的吗?在良好的语法和将引用保留到不必要的时间之间是否总是存在权衡?


答案 1

首先将您的成员分配给局部变量:

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

现在,lambda 仅捕获 中的局部变量。 不再捕获。getFn()MyClass.this

另一个选项(稍微详细一些)委托给帮助器方法:

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);
  }
}

答案 2

Lukas Eder 的局部-最终变量和帮助器-方法-委派解决方案的组合:

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);
  }
}