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

Noe*_*oel 13 java lambda functional-programming java-8

引用其封闭范围内的元素的Java lambda包含对其封闭对象的引用.一个人为的例子,lambda持有ref给MyClass:

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

如果lambda的寿命很长,这是有问题的; 然后我们得到一个长寿的MyClass引用,否则它会超出范围.在这里我们可以通过用私有静态类替换lambda来优化,这样我们只需要对我们需要的String进行引用,而不是对整个类:

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,这是超级冗长的,并且破坏了我们从lambdas中封闭范围中使用(有效最终)变量获得的良好语法.这在技术上是最佳的吗?是否总是在良好的语法和保持ref超过必要的可能性之间进行权衡?

Luk*_*der 15

首先将您的成员分配给本地变量:

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

现在,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);
  }
}
Run Code Online (Sandbox Code Playgroud)

  • @ AR.3:`foo`是lambda中的成员引用.每次调用lambda时都需要解析它.例如,如果在初始化`foo`之前从初始化程序调用lambda,则它可能是'null`.由于一些反射魔法,它可以再次改变. (2认同)
  • @AR.3:当 lambda 表达式直接访问 `MyClass.foo` 时,它*确实*使用编译时常量而不是实际读取字段。在 Java 中*从不*考虑字段尚未初始化或反射魔法的存在的可能性。但是,它仍然会捕获“this”,可能遵循某个规则,例如任何实例字段访问都要求捕获“this”,但是,我在 Java 语言规范中找不到该部分。 (2认同)
  • 我不会判断它是否与这个特定问题相关(尽管值得注意的是,与可能不同的现实生活代码相比,所编写的问题的行为可能有所不同)。但是,AR3 在第一条评论中提出了这一点,并且有一条评论指出它需要更正。后来我才注意到 AR.3 就此打开了一个新的 *特定* 问题,并在那里添加了 [更复杂的答案](http://stackoverflow.com/a/36608257/2711488)... (2认同)