有一个外部lambda包含内部lambda whis试图引用外部lambda中的var,它怎么可能? 我试着理解这个表达式在编译时会是什么样子。
public static void main(String[] args) {
ExecutorService service = Executors.newScheduledThreadPool(10);
DoubleStream.of(3.14159, 2.71828)
.forEach(c -> service.submit(
() -> System.out.println(c)));
我猜是会编译成这样的东西:
new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
new Runnable() {
@Override
public void run() {
System.out.println(aDouble);
}
};
}
};
我是对的吗?
答案 0 :(得分:3)
编译后的代码更接近
class MyClass {
public static void main(String[] args) {
ExecutorService service = Executors.newScheduledThreadPool(10);
DoubleStream.of(3.14159, 2.71828)
.forEach(new DoubleConsumer() {
@Override public void accept(double value) {
lambda1(service, value);
}
});
}
private static void lambda1(ExecutorService service, double c) {
service.submit(new Runnable() {
@Override public void run() {
lambda2(c);
}
});
}
private static void lambda2(double c) {
System.out.println(c);
}
}
不再有任何筑巢。实际上,编译后的代码不包含匿名内部类。将会生成运行时的类来完成接口DoubleConsumer
和Runnable
,这将调用这些合成方法来保存lambda表达式的主体。
但是这个草图已经足以证明“外部lambda”和“内部lambda”之间没有真正的技术差异。嵌套存在于源代码级别,允许您简明扼要地表达您的意图。
答案 1 :(得分:2)
最里面的lambda可以从其封闭的lambda访问c
,因为c
是effectively final
,因为没有任何东西可以分配给它。来自规范的链接部分:
- 处理方法,构造函数,lambda或异常参数(§8.4.1,§8.8.1,§9.4,§15.27.1,§14.20),以确定它是否真正是最终的,如声明符具有初始值设定项的局部变量。
然后上面
如果满足以下所有条件,则声明符具有初始值设定项(第14.4.2节)的局部变量实际上是最终的:
未宣布为最终版。
它永远不会出现在赋值表达式(第15.26节)的左侧。 (请注意,包含初始值设定项的局部变量声明符不是赋值表达式。)
它永远不会作为前缀或后缀增量或减量运算符的操作数出现(§15.14,§15.15)。
即使c
未明确final
,它仍然有效final
。
我猜是会编译成这样的东西:
...
我是对的吗?
我认为这是有效的;编译器实际上并没有这样做,lambdas是他们自己的事情。如果我们想要强调c
实际上是final
这一事实,我们可能会在其参数声明中添加final
。