我想知道这是否是一个实现细节......
在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();
}
Run Code Online (Sandbox Code Playgroud)
匿名的字节代码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
Run Code Online (Sandbox Code Playgroud)
如您所见,它捕获它加载的局部变量,但总是在运行时丢弃.
lambda做的大致相同,也捕获变量.
总是这样,还是实施细节?
规范在"引用"和"使用"变量之间没有区别.在这方面,你正在使用的变量调用t.yield(),尽管你调用一个static方法.对于这种情况,规范说
- 如果form是ExpressionName.[TypeArguments]标识符,然后:
- 如果是调用模式
static,则没有目标引用.计算ExpressionName,但结果将被丢弃.- 否则,目标引用是ExpressionName表示的值.
所以行为符合规范.
虽然很明显的是,评价一定会发生,当它有副作用,我也不会走这么远的结论是字节码序列ALOAD 0,GETFIELD,POP严格要求履行正式的规则,即变量进行评估,结果会被丢弃,因为这代码完全没有效果.
但无论这些指令是否存在,变量都会t被使用,因此必须符合形式要求,即它必须是最终有效的.
这种强制行为是否必须导致捕获内部类的实例中的值.为lambda表达式生成的类可能令人惊讶地完全没有指定.Java语言规范没有说明任何内容.
换句话说,当你询问价值捕获的角落情况,即被引用但不需要的变量的值时,即使是一般情况,即内部类始终保持对引用的引用的众所周知的规则.封闭this,即使不需要,虽然lambda表达式没有,但不会出现在官方规范中的任何地方.
| 归档时间: |
|
| 查看次数: |
175 次 |
| 最近记录: |