Java Lambda 引用封闭对象:替换为私有静态类?
Posted
技术标签:
【中文标题】Java Lambda 引用封闭对象:替换为私有静态类?【英文标题】:Java Lambda Referencing Enclosing Object: Replace with Private Static Class? 【发布时间】: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 比必要更长的可能性之间总是存在权衡吗?
【问题讨论】:
使用 C++ en.cppreference.com/w/cpp/language/lambda#Lambda_capture 我认为你的假设是不正确的。 lambda 不包含对 MyClass 的引用,仅包含对字符串的引用。 @PaulBoddington: That's not correct 我的立场是正确的。谢谢 Lukas 并向@Noel 道歉 @Ingo:this
被隐式引用
【参考方案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);
【讨论】:
以上是关于Java Lambda 引用封闭对象:替换为私有静态类?的主要内容,如果未能解决你的问题,请参考以下文章