man*_*uti 12 java string lambda java-8
这是基于这个问题.考虑这个示例,其中方法Consumer
基于lambda表达式返回:
public class TestClass {
public static void main(String[] args) {
MyClass m = new MyClass();
Consumer<String> fn = m.getConsumer();
System.out.println("Just to put a breakpoint");
}
}
class MyClass {
final String foo = "foo";
public Consumer<String> getConsumer() {
return bar -> System.out.println(bar + foo);
}
}
Run Code Online (Sandbox Code Playgroud)
我们知道,在进行函数式编程时引用lambda中的当前状态并不是一个好习惯,原因之一是lambda将捕获封闭的实例,在lambda本身超出范围之前不会进行垃圾收集.
但是,在这个与final
字符串相关的特定场景中,似乎编译器可能只是在返回的lambda中包含了constant(final
)字符串foo
(来自常量池),而不是MyClass
在调试时将所有实例括起来,如下所示(放置在的System.out.println
).是否与lambdas编译为特殊invokedynamic
字节码的方式有关?
Bri*_*etz 14
在你的代码中,bar + foo
真的是简写bar + this.foo
; 我们只是习惯了速记,忘记了我们隐含地获取了一个实例成员.所以你的lambda正在捕获this
,而不是this.foo
.
如果你的问题是"这个功能是否能以不同的方式实现",答案是"可能是的"; 我们本可以使lambda捕获的规范/实现更加复杂,目的是为各种特殊情况(包括这种情况)提供更好的性能.
更改规范以便我们捕获this.foo
而不是this
在性能方面不会发生太大变化; 它仍然是一个捕获lambda,这是一个比额外的字段解引用更大的成本考虑.所以我认为这不会提供真正的性能提升.
如果 lambda 捕获foo
而不是this
,则在某些情况下可能会得到不同的结果。考虑以下示例:
public class TestClass {\n public static void main(String[] args) {\n MyClass m = new MyClass();\n m.consumer.accept("bar2");\n }\n}\n\nclass MyClass {\n final String foo;\n final Consumer<String> consumer;\n\n public MyClass() {\n consumer = getConsumer();\n // first call to illustrate the value that would have been captured\n consumer.accept("bar1");\n foo = "foo";\n }\n\n public Consumer<String> getConsumer() {\n return bar -> System.out.println(bar + foo);\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n输出:
\n\nbar1null\nbar2foo\n
Run Code Online (Sandbox Code Playgroud)\n\n如果foo
被 lambda 捕获,它将被捕获为null
,第二次调用将打印bar2null
。然而,由于MyClass
实例被捕获,它会打印正确的值。
当然,这是丑陋的代码并且有点做作,但在更复杂的现实代码中,这样的问题可能很容易发生。
\n\n请注意,唯一真正丑陋的事情是我们foo
通过消费者强制读取构造函数中要分配的内容。foo
构建消费者本身预计不会在那时读取,因此foo
只要您不立即使用它,在分配 \xe2\x80\x93 之前构建它仍然是合法的。
但是,在分配 \xe2\x80\x93 之前,编译器不会让您consumer
在构造函数中初始化相同的内容foo
,这可能是最好的:-)
归档时间: |
|
查看次数: |
646 次 |
最近记录: |