Java“空白的final字段可能尚未初始化”匿名接口与Lambda表达式

Java“空白的final字段可能尚未初始化”匿名接口与Lambda表达式,第1张

Java“空白的final字段可能尚未初始化”匿名接口与Lambda表达式

我无法使用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
调用时已完全初始化。



欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5499723.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-13
下一篇 2022-12-12

发表评论

登录后才能评论

评论列表(0条)

保存