作者:itsima | 来源:互联网 | 2022-11-30 13:23
我想知道这是否是一个实现细节......
在Java中,为匿名类和lambdas捕获使用的局部变量.对于匿名类,this
无论是否需要,都会在非静态上下文中捕获.
但是,即使未用于Oracle JDK 8更新181,也会捕获引用的任何本地变量.
public static void main(String[] args) {
Thread t = Thread.currentThread();
Runnable run = new Runnable() {
@Override
public void run() {
t.yield();
}
};
Runnable run2 = () -> t.yield();
run.run();
run2.run();
}
匿名的字节代码Runnable
是
// access flags 0x1
public run()V
L0
LINENUMBER 8 L0
ALOAD 0
GETFIELD UnusedLocalVariable$1.val$t : Ljava/lang/Thread;
POP
INVOKESTATIC java/lang/Thread.yield ()V
L1
LINENUMBER 9 L1
RETURN
L2
LOCALVARIABLE this LUnusedLocalVariable$1; L0 L2 0
MAXSTACK = 1
MAXLOCALS = 1
如您所见,它捕获它加载的局部变量,但总是在运行时丢弃.
lambda做的大致相同,也捕获变量.
总是这样,还是实施细节?
1> Holger..:
规范在"引用"和"使用"变量之间没有区别.在这方面,你正在使用的变量调用t.yield()
,尽管你调用一个static
方法.对于这种情况,规范说
JLS§15.12.4.1,15.12.4.1.计算目标参考(如有必要):
如果form是ExpressionName.[TypeArguments]标识符,然后:
如果是调用模式static
,则没有目标引用.计算ExpressionName,但结果将被丢弃.
否则,目标引用是ExpressionName表示的值.
所以行为符合规范.
虽然很明显的是,评价一定会发生,当它有副作用,我也不会走这么远的结论是字节码序列ALOAD 0
,GETFIELD
,POP
严格要求履行正式的规则,即变量进行评估,结果会被丢弃,因为这代码完全没有效果.
但无论这些指令是否存在,变量都会t
被使用,因此必须符合形式要求,即它必须是最终有效的.
这种强制行为是否必须导致捕获内部类的实例中的值.为lambda表达式生成的类可能令人惊讶地完全没有指定.Java语言规范没有说明任何内容.
换句话说,当你询问价值捕获的角落情况,即被引用但不需要的变量的值时,即使是一般情况,即内部类始终保持对引用的引用的众所周知的规则.封闭this
,即使不需要,虽然lambda表达式没有,但不会出现在官方规范中的任何地方.