Java 8 - lambda 捕获机制 : 使用局部变量

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_15071263/article/details/102390699

Java 8 - lambda 捕获机制 : 使用局部变量


1、概念

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

2、例子

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

int portNumber = 1337; 
Runnable r = () -> System.out.println(portNumber); 

3、局限

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

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

int portNumber = 1337; 
Runnable r = () -> System.out.println(portNumber); 
int portNumber = 1338; 

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

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

第一

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

第二

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

5、和闭包比较

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

闭包的简单定义:

用科学的说法来说,闭包就是一个函数的实例,且它可以无限 制地访问那个函数的非本地变量。例如,闭包可以作为参数传递给另一个函数。它也可以访 问和修改其作用域之外的变量。

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

猜你喜欢

转载自blog.csdn.net/qq_15071263/article/details/102390699
今日推荐