且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Lambda表达式和变量捕获

更新时间:2023-11-11 12:08:52

首先,我们可以看看 JLS ,其中指出以下内容:

At first, we can take a look at the JLS, which states the following:


任何使用的局部变量,形式参数或异常参数但是没有在lambda表达式中声明必须被声明为final或有效地为final(§4.12.4),否则在尝试使用时出现编译时错误。

Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression must either be declared final or be effectively final (§4.12.4), or a compile-time error occurs where the use is attempted.

在lambda主体之前使用但未声明的任何局部变量必须在lambda主体之前被明确赋值(§16(确定赋值))或编译时错误。

Any local variable used but not declared in a lambda body must be definitely assigned (§16 (Definite Assignment)) before the lambda body, or a compile-time error occurs.

变量使用的类似规则适用于内部类的体(§8.1.3)。对有效的最终变量的限制禁止访问动态变化的局部变量,其捕获可能引入并发问题。与最终限制相比,它减少了程序员的文书负担。

Similar rules on variable use apply in the body of an inner class (§8.1.3). The restriction to effectively final variables prohibits access to dynamically-changing local variables, whose capture would likely introduce concurrency problems. Compared to the final restriction, it reduces the clerical burden on programmers.

有效的最终变量的限制包括标准循环变量,但不是增强型循环变量,被视为循环的每次迭代都是不同的(§14.14.2)。

The restriction to effectively final variables includes standard loop variables, but not enhanced-for loop variables, which are treated as distinct for each iteration of the loop (§14.14.2).






要更好地了解它,请查看此示例类:


To understand it better, take a look at this example class:

public class LambdaTest {

    public static void main(String[] args) {
        LambdaTest test = new LambdaTest();
        test.returnConsumer().accept("Hello");
        test.returnConsumerWithInstanceVariable().accept("Hello");
        test.returnConsumerWithLocalFinalVariable().accept("Hello");
    }

    String string = " world!";

    Consumer<String> returnConsumer() {
        return ((s) -> {System.out.println(s);});
    }

    Consumer<String> returnConsumerWithInstanceVariable() {
        return ((s) -> {System.out.println(s + string);});
    }

    Consumer<String> returnConsumerWithLocalFinalVariable() {
        final String foo = " you there!";
        return ((s) -> {System.out.println(s + foo);});
    }

}

主要输出是

Hello
Hello world!
Hello you there!

这是因为在这里返回一个lambda与使用 new Consumer< String>(){...} 。您的lambda - Consumer< String> 的实例具有对其已创建的类的引用。您可以重写returnConsumerWithInstanceVariable以使用 System。 out.println(s + LambdaTest.this.string),这样做完全一样。这就是为什么你被允许访问(和修改)实例变量。

This is because returning a lambda here is much the same as creating a new anonymous class with new Consumer<String>() {...}. Your lambda - an instance of Consumer<String> has a reference to the class it has been created in. You could rewrite the returnConsumerWithInstanceVariable to use System.out.println(s + LambdaTest.this.string), this would do exactly the same. This is why you are allowed to to access (and modify) instance variables.

如果你的方法中有一个(有效的)最终局部变量,你可以访问它,因为您可以访问它,因为它被复制到您的lambda实例。

If you have a (effective) final local variable in your method, you can access it because you can access it because it gets copied to your lambda instance.

但是,如果它不是最终的,你认为应该在以下环境中发生什么: p>

But, however, if its not final, what do you think should happen in the following context:

Consumer<String> returnConsumerBad() {
    String foo = " you there!";
    Consumer<String> results = ((s) -> {System.out.println(s + foo);});
    foo = " to all of you!";
    return results;
}

应该将值复制到您的实例,但是当局部变量是否更新?这可能会导致混乱,因为我认为很多程序员会希望foo在返回这个lambda后会有新的值给所有的人。

Should the value get copied to your instance, but then not get updated when the local variable is updated? That would probably cause confusion, as I think many programmers would expected foo to have the new value "to all of you" after returning this lambda.

如果你有一个原始的价值,它会放在堆栈上。所以你不能简单地引用局部变量,因为它可以在你的方法结束后消失。

If you had a primitive value, it would lay on the stack. So you could not simply reference the local variable, because it could disappear after the end of your method is reached.