总是在Java中捕获未使用的引用变量

时间:2018-11-08 10:16:46

标签: java java-8

我想知道这是否是实施细节...

在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的功能大致相同,还捕获了变量。

是否总是这样,还是实现细节?

1 个答案:

答案 0 :(得分:7)

该规范在“引用”和“使用”变量之间没有区别。在这方面,尽管您正在调用t.yield()方法,但您通过调用static 使用变量。对于这种情况,规范说

JLS §15.12.4.1, 15.12.4.1. Compute Target Reference (If Necessary)
  
      
  • 如果表单为 ExpressionName。 [TypeArguments]标识符,然后:      
        
    • 如果调用方式为static,则没有目标引用。计算 ExpressionName ,但结果将被丢弃。
    •   
    • 否则,目标引用是由 ExpressionName 表示的值。
    •   
  •   

所以行为符合规范。

虽然很明显必须进行评估,但有副作用时,我不会断定严格要求字节码序列ALOAD 0GETFIELDPOP履行正式的规则,即对变量进行求值并丢弃结果,因为该代码完全无效。

但是无论这些指令是否存在,都使用变量t,因此要遵守正式的要求,即变量必须是 final有效的

此强制行为是否必须导致捕获内部类resp实例内的值。令人惊讶的是,为lambda表达式生成的类是完全未指定的。 Java语言规范未对此说任何话。

换句话说,当您询问值捕获的特殊情况时,即被引用但不需要的变量的值,甚至是一般情况,即众所周知的规则,即内部类始终保留对封包this的引用,即使不需要,尽管不需要lambda表达式,但在官方规范中都没有出现。