通常,我们希望能够在lambda表达式的闭合方法或类中访问其他的变量,例如:
package java8test; public class T1 { public static void main(String[] args) { repeatMessage("Hello", 20); } public static void repeatMessage(String text,int count){ Runnable r = () -> { for(int i = 0; i < count; i++){ System.out.println(text); Thread.yield(); } }; new Thread(r).start(); } }
注意看lambda表达式中的变量count和text,它们并没有在lambda表达式中被定义,而是方法repeatMessage的参数变量。如果你思考一下,就会发现这里有一些隐含的东西。lambda表达式可能会在repeatMessage返回之后才运行,此时参数变量已经消失了。如果保留text和count变量会怎样呢?
为了理解这一点,我们需要对lambda表达式有更深入的理解。一个lambda表达式包括三个部分:
- 一段代码
- 参数
- 自由变量的值,这里的“自由”指的是那些不是参数并且没有在代码中定义的变量。
在我们的示例中,lambda表达式有两个自由变量,text和count。数据结构表示lambda表达式必须存储这两个变量的值,即“Hello”和20。我们可以说,这些值已经被lambda表达式捕获了(这是一个技术实现的细节。例如,你可以将一个lambda表达式转换为一个只含一个方法的对象,这样自由变量的值就会被复制到该对象的实例变量中)。
注意:含有自由变量的代码块才被称之为“闭包(closure)”。在Java中,lambda表达式就是闭包。事实上,内部类一直都是闭包。Java8中为闭包赋予了更吸引人的语法。
如你所见,lambda表达式可以捕获闭合作用域中的变量值。在java中,为了确保被捕获的值是被良好定义的,需要遵守一个重要的约束。在lambda表达式中,被引用的变量的值不可以被更改。例如,下面这个表达式是不合法的:
public static void repeatMessage(String text,int count){ Runnable r = () -> { while(count > 0){ count--; //错误,不能更改已捕获变量的值 System.out.println(text); Thread.yield(); } }; new Thread(r).start(); }
时间: 2024-10-13 23:25:12