我无法使用Eclipse的编译器重现您最后一个案例的错误。
但是,我可以想象的Oracle编译器的理由如下:在lambda中,
obj必须在声明时捕获的值。也就是说,在lambda主体中声明它时必须对其进行初始化。
但是,在这种情况下,Java应该捕获
Foo实例的值而不是
obj。然后,它可以
obj通过(初始化的)
Foo对象引用进行访问并调用其方法。Eclipse编译器就是这样编译您的代码。
这在规范中有所暗示,在这里:
方法参考表达式评估的时间比lambda表达式(第15.27.4节)要复杂。当方法引用表达式的::分隔符之前具有表达式(而不是类型)时,将立即对该子表达式求值。
存储评估结果,直到调用相应功能接口类型的方法为止
;此时,结果将用作调用的目标参考。这意味着::分隔符之前的表达式仅在程序遇到方法引用表达式时才被评估,并且不会在后续对功能接口类型的调用时被重新评估。
类似的事情发生了
Object obj = new Object(); // imagine some local variableRunnable run = () -> { obj.toString(); };
想象一下
obj,当执行lambda表达式代码时,它是一个局部变量,将
obj被求值并生成一个引用。此引用存储在
Runnable创建的实例的字段中。当
run.run()被调用时,例如使用存储的参考值。
如果
obj未初始化,则不会发生这种情况。例如
Object obj; // imagine some local variableRunnable run = () -> { obj.toString(); // error};
Lambda无法捕获的值
obj,因为它尚无值。它实际上等效于
final Object anonymous = obj; // won't work if obj isn't initializedRunnable run = new AnonymousRunnable(anonymous);...class AnonymousRunnable implements Runnable { public AnonymousRunnable(Object val) { this.someHiddenRef = val; } private final Object someHiddenRef; public void run() { someHiddenRef.toString(); }}
这就是Oracle编译器当前如何运行您的代码片段。
但是,Eclipse编译器不是捕获的值
obj,而是捕获
this(
Foo实例的)值。它实际上等效于
final Foo anonymous = Foo.this; // you're in the Foo constructor so this is valid reference to a Foo instanceRunnable run = new AnonymousRunnable(anonymous);...class AnonymousRunnable implements Runnable { public AnonymousRunnable(Foo foo) { this.someHiddenRef = foo; } private final Foo someHiddenFoo; public void run() { someHiddenFoo.obj.toString(); }}
这很好,因为您假设
Foo实例在
run调用时已完全初始化。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)