Java 内部lambda参考外部lambda变量
有一个包含内部lambda的外部lambda,它试图引用外部lambda中的var,这怎么可能呢? 我试图理解这个表达式在编译时的样子,使之成为可能Java 内部lambda参考外部lambda变量,java,lambda,reference,functional-programming,java-8,Java,Lambda,Reference,Functional Programming,Java 8,有一个包含内部lambda的外部lambda,它试图引用外部lambda中的var,这怎么可能呢? 我试图理解这个表达式在编译时的样子,使之成为可能 public static void main(String[] args) { ExecutorService service = Executors.newScheduledThreadPool(10); DoubleStream.of(3.14159, 2.71828) .forEach(c ->
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);
}
};
}
};
我说得对吗?最里面的lambda可以从它的封闭lambda访问c,因为c是,因为没有任何东西分配给它。从等级库的链接部分: 为了确定方法、构造函数、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 我猜威尔会编译成这样:
new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
new Runnable() {
@Override
public void run() {
System.out.println(aDouble);
}
};
}
};
我说得对吗
我认为事实上就是这样;编译器实际上并没有这样做,lambda是它们自己的东西。如果我们想强调c实际上是final这一事实,我们可以将final添加到它的参数声明中。最内层的lambda可以从它的封闭lambda访问c,因为c是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 我猜威尔会编译成这样:
new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
new Runnable() {
@Override
public void run() {
System.out.println(aDouble);
}
};
}
};
我说得对吗
我认为事实上就是这样;编译器实际上并没有这样做,lambda是它们自己的东西。如果我们想强调c实际上是final这一事实,我们可以在它的参数声明中添加final
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”之间没有真正的技术区别。嵌套存在于源代码级别,允许您简洁地表达您的意图
另请参见“”编译后的代码更接近
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”之间没有真正的技术区别。嵌套存在于源代码级别,允许您简洁地表达您的意图
另请参见“”Yes,这与使用匿名内部类是等效的。但是编译器不会将lambda编译为匿名内部类。您还错过了Runnable提交给服务的部分。为什么您认为作为“内部lambda”或“外部lambda”有任何关联?您的“外部lambda”正在访问服务,这并不困扰您,所以当“内部lambda”访问c时为什么会这样做呢?是的,这与使用匿名内部类是等效的。但是编译器不会将lambda编译为匿名内部类。您还错过了Runnable提交给服务的部分。为什么您认为作为“内部lambda”或“外部lambda”有任何关联?您的“外部lambda”正在访问服务,这并不困扰您,所以当“内部lambda”访问c时,为什么会这样呢?嗨,Holger。新的DoubleConsumer{…}与其他lambda类似,java编译器将 评级为静态方法,如lambdaExecutorService,在封闭类中为double。嗨,Holger。新的DoubleConsumer{…}与其他lambda类似,java编译器将在封闭类中生成一个静态方法,如lambdaExecutorService,double。