我想知道这是否是实施细节...
在Java中,捕获的局部变量用于匿名类和lambda。对于匿名类,无论是否需要,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的功能大致相同,还捕获了变量。
是否总是这样,还是实现细节?
答案 0 :(得分:7)
该规范在“引用”和“使用”变量之间没有区别。在这方面,尽管您正在调用t.yield()
方法,但您通过调用static
使用变量。对于这种情况,规范说
- 如果表单为 ExpressionName。 [TypeArguments]标识符,然后:
- 如果调用方式为
static
,则没有目标引用。计算 ExpressionName ,但结果将被丢弃。- 否则,目标引用是由 ExpressionName 表示的值。
所以行为符合规范。
虽然很明显必须进行评估,但有副作用时,我不会断定严格要求字节码序列ALOAD 0
,GETFIELD
,POP
履行正式的规则,即对变量进行求值并丢弃结果,因为该代码完全无效。
但是无论这些指令是否存在,都使用变量t
,因此要遵守正式的要求,即变量必须是 final有效的。
此强制行为是否必须导致捕获内部类resp实例内的值。令人惊讶的是,为lambda表达式生成的类是完全未指定的。 Java语言规范未对此说任何话。
换句话说,当您询问值捕获的特殊情况时,即被引用但不需要的变量的值,甚至是一般情况,即众所周知的规则,即内部类始终保留对封包this
的引用,即使不需要,尽管不需要lambda表达式,但在官方规范中都没有出现。