【问题标题】:Java Lambda Referencing Enclosing Object: Replace with Private Static Class?Java Lambda 引用封闭对象:替换为私有静态类?
【发布时间】:2016-07-11 07:00:08
【问题描述】:

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

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

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

不幸的是,这是非常冗长的,并且破坏了我们通过在 lambda 中使用(实际上是最终的)变量获得的漂亮语法。这在技术上是最优的吗?在良好的语法和保持 ref 比必要更长的可能性之间总是存在权衡吗?

【问题讨论】:

标签: java lambda functional-programming java-8


【解决方案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);
  }
}

【讨论】:

  • @AR.3: foo 是 lambda 中的成员引用。每次调用 lambda 时都需要解决它。例如,如果在初始化 foo 之前从初始化程序调用了 lambda,则它可能是 null。由于一些反射魔法,它可以再次更改。
  • @srborlongan:除了可读性之外,我没有看到任何缺点?高阶 lambdas 还不是很常见(目前)
  • @AR.3:当 lambda 表达式直接访问 MyClass.foo 时,它确实使用编译时常量,而不是实际读取字段。在 Java 中从未考虑过字段尚未初始化或反射魔法存在的可能性。但是,它仍然捕获this,可能要遵循某个规则,例如任何实例字段访问都要求捕获this,但是,我在Java语言规范中找不到该部分。
  • @Lukas Eder:当localFoo 被声明为final 时,foo 字段将永远不会在运行时被读取。相反,lambda 表达式将直接使用"foo",而不管在您调用getFn()foo 真正包含的内容(当然是第一个解决方案)。即使没有将localFoo声明为final,也会直接用"foo"初始化,而不是实际读取foo
  • 我不判断它是否与这个特定问题相关(尽管值得注意的是,与可能不同的实际代码相比,所写问题的行为可能不同)。然而,AR3 在第一条评论中提出了这一点,并且有一条评论指出它需要更正。后来我才注意到 AR.3 对此提出了一个新的特定问题,并在那里添加了一个more complex answer...
【解决方案2】:

Lukas Eder 的 local-final-variable 和 helper-method-delegation 解决方案的组合:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-08
    • 1970-01-01
    • 2020-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 2022-11-20
    相关资源
    最近更新 更多