Java lambda 使用变量 java lambda 局部变量_实例变量

1、概念

我们迄今为止所介绍的所有Lambda表达式都只用到了其主体里面的参数。但Lambda表达式 也允许使用自由变量(不是参数,而是在外层作用域中定义的变量),就像匿名类一样。它们被 称作捕获Lambda

2、例子

例如,下面的Lambda捕获了portNumber变量

3、局限

尽管如此,还有一点点小麻烦:关于能对这些变量做什么有一些限制。Lambda可以没有限 制地捕获(也就是在其主体中引用)实例变量和静态变量。但局部变量必须显式声明为final, 或事实上是final。换句话说,Lambda表达式只能捕获指派给它们的局部变量一次。(注:捕获实例变量可以被看作捕获最终局部变量this。)

如果这么写,则会编译失败,因为portNumber 不是隐式final 的,他被赋值了2次

Java lambda 使用变量 java lambda 局部变量_实例变量_02

4、为什么局部变量会有final 这个限制

为什么局部变量有这些限制。

第一

实例变量和局部变量背后的实现有一 个关键不同。实例变量都存储在堆中,而局部变量则保存在栈上。如果Lambda可以直接访问局部变量,而且Lambda是在一个线程中使用的,则使用Lambda的线程,可能会在分配该变量的线 程将这个变量收回之后,去访问该变量。因此,Java在访问自由局部变量时,实际上是在访问它的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了 这个限制。

第二

这一限制不鼓励你使用改变外部变量的典型命令式编程模式,这种模式会阻碍很容易做到的并行处理 ,这是java 8 所不希望看到的结果。

5、和闭包比较

你可能已经听说过闭包(closure)这个词,你可能会想 Lambda是否满足闭包的定义。

闭包的简单定义:

Java lambda 使用变量 java lambda 局部变量_作用域_03

现在,Java 8的Lambda和匿名类可以做类似于闭包的事情:它们可以作为参数传递给方法,并且可以访问其作用域之外的变量。但有一个限制:它们不 能修改定义Lambda的方法的局部变量的内容。这些变量必须是隐式最终的。可以认为Lambda 是对值封闭,而不是对变量封闭。这种限制存在的原因在于局部变量保存在栈上, 并且隐式表示它们仅限于其所在线程。如果允许捕获可改变的局部变量,就会引发造成线程 不安全的新的可能性,而这是我们不想看到的(实例变量可以,因为它们保存在堆中,而堆 是在线程之间共享的) 。

编辑整理 丨贾子涵