如何使用IntelliJ的评估表达式工具与lambdas和外部范围?

时间:2015-01-22 18:33:51

标签: java intellij-idea lambda java-8

这是一个简单的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的“评估表达式”工具来评估outerScope的定义。

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

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

有没有办法解决这个问题?

1 个答案:

答案 0 :(得分:3)

Lambda表达式被编译为拥有类中的合成方法。当我在课堂上运行javap -private -l时,此方法打印为:

private static void lambda$main$0(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中修复。{ / p>

如果您不能等待并且不想编写修补LocalVariableTable属性的工具,您可能会认为合成方法的调用方知道参数值。因此,如果您在堆栈跟踪中向上一步到运行时生成的lambda实例,您将看到所有捕获的值作为实例变量,尽管它们将生成名称,如arg$1等。