lambda表达式中,可以访问其外层作用域中定义的变量.例如,lambda表达式可以使用其外层类定义的实例或静态变量.lambda表达式也可以显式或隐式的访问this变量,该变量引用lambda表达式的外层类的调用实例.因此,lambda表达式可以获取或设置其外层类的实例或静态变量的值,以及调用其外层类定义的方法.
但是,当lambda表达式使用其外层作用域内定义的局部变量时,会产生一种特殊的情况,称为变量捕获(variable capture).在这种情况下,lambda表达式只能使用实质上为final的局部变量.实质上final变量是指第一次赋值以后,值不发生变化的变量.没有必要显示的将这种变量声明为final,不过那样做也不是错误(外层作用域的this参数自动是实质上final变量,lambda表达式没有自己的this参数)
lambda表达式不能修改外层作用域域内的局部变量,理解这一点很重要.修改局部变量会移除其实质上的final状态,从而使捕获该变量变得不合法.
下面的程序演示了实质上final的局部变量和可变局部变量(mutable local variable)的区别:
public class test2 { public static void main(String args[]) { int num = 10; MyFunc mylambda = (n)->{ int v = num + n; return v; }; //num=9; Local variable num defined in an enclosing scope must be final or effectively final System.out.println("8 : "+mylambda.func(8)); } } interface MyFunc { int func(int n); }
正如注释指出的,num实质上是final变量,所以可以在mylambda内使用,这就是为什么println语句输出数字18的原因所在.当通过实参调用func()时,lambda中v的值是通过将num(这里为10)添加到传给n的值(这里为8)来设置的.因此,func()的返回值为18.但是,如果修改了num,不管是在lambda表达式内还是lambda表达式外,num都会丢失其实质上final的状态,这会导致发生错误,程序将无法通过编译.
需要重点强调的是,lambda表达式可以使用和修改其调用类的实例变量,只是不能使用其外层作用域中的局部变量,除非该变量实质上是final.