Java中始终捕获未使用的引用变量
我想知道这是否是一个实现细节 在Java中,为匿名类和lambda捕获使用的局部变量。对于匿名类,无论是否需要,Java中始终捕获未使用的引用变量,java,java-8,Java,Java 8,我想知道这是否是一个实现细节 在Java中,为匿名类和lambda捕获使用的局部变量。对于匿名类,无论是否需要,此也在非静态上下文中捕获 然而,即使未用于Oracle JDK 8更新181,也会捕获引用的任何局部变量 public static void main(String[] args) { Thread t = Thread.currentThread(); Runnable run = new Runnable() { @Override
此
也在非静态上下文中捕获
然而,即使未用于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()
来使用变量,尽管您调用的是静态方法。对于这种情况,规范说
:
- 如果表单是ExpressionName。[TypeArguments]标识符,然后:
- 如果调用模式是静态的,则没有目标引用。将计算ExpressionName,但随后将丢弃结果
- 否则,目标引用是由ExpressionName表示的值
因此,行为符合规范
虽然很明显,评估必须发生,但当它有副作用时,我不会得出这样的结论:字节码序列ALOAD 0
,GETFIELD
,POP
是严格要求的,以满足评估变量并丢弃结果的正式规则,因为那个代码根本没有作用
但是,无论这些指令是否存在,都会使用变量t
,因此必须符合正式要求,即它必须是有效的最终指令
此强制行为是否必须导致捕获内部类resp实例中的值。可能令人惊讶的是,为lambda表达式生成的类完全没有指定。Java语言规范对此没有任何说明
换句话说,当您询问值捕获的一个角情况时,即被引用但不需要的变量的值,甚至是一般情况,即众所周知的规则,即内部类始终保留对封闭的this
的引用,即使不需要,而lambda表达式不需要,没有出现在官方规范的任何地方。这被标记为Java 8,但它是否特定于Java 8?如中所述,您是否在最近的任何项目上进行了测试,或者您是否有任何理由相信现在的行为有所不同?t
只是意外地未使用,因为Thread.yield()
是一种静态方法。如果要调用t.setName(“someName”)
则需要捕获t
。也许java编译器不够聪明,无法避免捕获“表面上使用过,但实际上未使用过”的局部变量。@ThomasKläger我可以理解这是一种Oracle java C不做的优化,但这是因为它是不允许的,例如,像这个,或者因为它不平凡。@ThomasKläger它可以,但是x
不是final
,所以它不是。当您将x
声明为final boolean x=false时代码>并有一个内部类/lambda表达式执行if(x)System.out.println(y)编译器将优化代码,生成唯一的返回
指令,但仍然捕获y
。不同的问题,但相关的问题是:除了您之外,还有谁会对此进行澄清?