如何将 IntelliJ 的 Evaluate Expression 工具与 lambda 和外部作用域一起使用?

How to use IntelliJ's Evaluate Expression tool with lambdas and outer scopes?

这是一个简单的 Java 8 lambda 示例。

public class Main {
    public static void main(String[] args){
        String outerScope = "outer";
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                String runnableInner = "runnable inner";
                System.out.println("inside runnable: " + outerScope);
                Void avoid = null; //Breakpoint
            }
        };
        Runnable lambda = () -> {
            String lambdaInner = "lambda inner";
            System.out.println("inside lambda: " + outerScope);
            Void avoid = null; //Breakpoint
        };
        runnable.run();
        lambda.run();
    }
}

runnable 范围内,可以使用 IntelliJ 的 "Evaluate Expression" 工具来评估 outerScope 是否已定义。

但是,在 lambda 范围内 outerScope 是未定义的。

两个打印语句都正确打印了 outerscope 的值。

有办法解决这个问题吗?

Lambda 表达式被编译成拥有 class 的合成方法。当我运行javap -private -l和你的class时,这个方法打印为:

private static void lambda$main[=10=](java.lang.String);
  LineNumberTable:
    line 18: 0
    line 19: 3
    line 20: 28
    line 21: 30
  LocalVariableTable:
    Start  Length  Slot  Name   Signature
        3      28     1 lambdaInner   Ljava/lang/String;
       30       1     2 avoid   Ljava/lang/Void;

您在这里可以看到,该方法有一个参数,该参数将接收 outerScope 变量的实际捕获值。但是 LocalVariableTable 没有为其声明名称,这很可能是调试器不显示其 运行 时间值的原因。

具有讽刺意味的是,这意味着如果我们删除 LocalVariableTable 属性,我们将看到所需的值。例如。当使用 -g:lines 编译并在 lambda 表达式主体中的断点处停止时,Netbeans 调试器显示:

args    String  "outer"
Variable information not available, source compiled without -g option           

所以我们知道看到所有值作为参数传递,代价是看不到在方法内声明的其他局部变量。


所以需要的是一个 LocalVariableTable 属性,其中包含捕获值的外部作用域变量名称的名称,这个问题必须在 javac 中解决,这将 apparently happen in jdk1.8u60 .

如果你迫不及待也不想编写工具来修补 LocalVariableTable 属性,你可以考虑合成方法的 caller 知道参数值。因此,如果您在堆栈跟踪中向上移动到 运行time 生成的 lambda 实例,您将看到所有捕获的值作为实例变量,尽管它们将生成诸如 arg 等的名称