Java 内部lambda参考外部lambda变量

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 ->

有一个包含内部lambda的外部lambda,它试图引用外部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);
            }
        };
    }
};

我说得对吗?

最里面的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。