我想知道这是否是一个实现细节...
在 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 的作用大致相同,也捕获变量。
总是这样吗,还是一个实现细节?
最佳答案
规范对“引用”和“使用”变量没有区别。在这方面,您是通过调用 t.yield()
使用变量,尽管您正在调用 static
方法。对于这种情况,规范说
- If form is ExpressionName . [TypeArguments] Identifier, then:
- If the invocation mode is
static
, then there is no target reference. The ExpressionName is evaluated, but the result is then discarded.- Otherwise, the target reference is the value denoted by ExpressionName.
因此行为符合规范。
虽然很明显评估必须发生,但当它有副作用时,我不会得出结论字节码序列 ALOAD 0
, GETFIELD
, POP
严格要求满足评估变量并丢弃结果的正式规则,因为该代码根本没有效果。
但无论这些指令是否存在,变量 t
都会被使用,因此需要遵守形式要求,即它必须是 effective final。
此强制行为是否必须导致捕获内部类实例中的值。为 lambda 表达式生成的类可能令人惊讶地完全未指定。 Java 语言规范对此没有任何说明。
换句话说,当您询问值捕获的极端情况时,即被引用但不需要的变量的值,即使是一般情况,即众所周知的内部类始终保持对封闭的 this
的引用,即使不需要,而 lambda 表达式不需要,也不会出现在官方规范中的任何地方。
关于java - 未使用的引用变量总是在 Java 中捕获,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53205627/