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

标签 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 的作用大致相同,也捕获变量。

总是这样吗,还是一个实现细节?

最佳答案

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

JLS §15.12.4.1, 15.12.4.1. Compute Target Reference (If Necessary) :
  • 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/

相关文章:

java - 如何从java中的哈希列表中检索值

java - tableModel.addRow() 未在 JTable 中显示任何内容,但行对象具有值

java - IntelliJ 在流中看到对象而不是正确的类类型

java - 在 Java8 中使用 lambda 仅在不为空时过滤值

java - 返回两个可选值的总和,如果至少有一个不存在,则返回 null

java - 如何在 Java 中实现谓词,用于根据任意数量的规则交叉检查字符串?

java - 由于依赖关系,无法将 World Wind Java 用于 Maven 项目

java - 与 Java 驱动程序一起使用时,如何在 Selenium 中触发链接的单击事件?

java - addActionListener 中的递归?

java - 如何在运行时为当前测试ApplicationContext注册Spring Context事件