java final 初始化_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 variable

Runnable run = () -> {

obj.toString();

};

想象一下obj,当执行lambda表达式代码时,它是一个局部变量,将obj被求值并生成一个引用。此引用存储在Runnable创建的实例的字段中。当run.run()被调用时,例如使用存储的参考值。

如果obj未初始化,则不会发生这种情况。例如

Object obj; // imagine some local variable

Runnable run = () -> {

obj.toString(); // error

};

Lambda无法捕获的值obj,因为它尚无值。它实际上等效于

final Object anonymous = obj; // won't work if obj isn't initialized

Runnable 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 instance

Runnable 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调用时已完全初始化。

2020-09-28

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值