这是一个简单的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
的值。
有没有办法解决这个问题?
答案 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
等。