前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Lambda表达式和闭包Closure

Lambda表达式和闭包Closure

作者头像
程序那些事
发布2020-07-08 10:04:07
9640
发布2020-07-08 10:04:07
举报
文章被收录于专栏:程序那些事

简介

我们通常讲到闭包,一般都是指在javascript的环境中。闭包是JS中一个非常重要的也非常常用的概念。

闭包产生的原因就是变量的作用域范围不同。一般来说函数内部的定义的变量只有函数内部可见。如果我们想要在函数外部操作这个变量就需要用到闭包了。

JS中的闭包

在JS中,变量可以分为两种全局作用域和局部作用域。在函数外部无法读取函数内部定义的局部变量。

代码语言:javascript
复制
function f1(){    
var n=10;  
}  
alert(n); // error

上面的例子中,我们在函数f1中定义了一个局部变量n,然后尝试从函数外部访问它。结果出错。

虽然函数中定义的变量在函数外部无法被访问。但是在函数中定义的函数中可以访问呀。

代码语言:javascript
复制
function f1(){    
var n=10;    
function f2(){
      alert(n);    
}    
return f2;  
}  
var result=f1();  
result(); // 10

上面的例子中,我们在f1中定义了f2,在f2中访问了局部变量n。最后将f2返回。接着我们可以操作返回的函数f2来对函数中定义的局部变量n进行操作。

所以我们得出了闭包的定义:闭包就是定义在函数内部的函数,或者闭包是能够访问函数局部变量的函数。

java中的闭包

在lambda表达式出现之前,java中是没有函数的概念的。和函数差不多相当的就是方法了。

在方法内部可以定义方法的局部变量。我们无法在方法内部定义方法,但是我们可以在方法内部定义匿名类。那么这个匿名类是可以访问方法中定义的局部变量的。如下例所示:

代码语言:javascript
复制
public Runnable createClosureUsingClass(){
        int count=10;
        Runnable runnable= new Runnable() {
            @Override
            public void run() {
                System.out.println(count);
            }
        };
        return runnable;
    }

在上面的方法中,我们定义了一个局部变量count。然后创建了一个匿名类runnable。在runnable中,我们访问了局部变量count。

最后将这个创建的匿名类返回。这样返回的匿名类就包含了对方法局部变量的操作,这样就叫做闭包。

在Lambda表达式最佳实践中,我们介绍了lambda表达式和匿名类的不同之处在于:

在内部类中,会创建一个新的作用域范围,在这个作用域范围之内,你可以定义新的变量,并且可以用this引用它。

但是在Lambda表达式中,并没有定义新的作用域范围,如果在Lambda表达式中使用this,则指向的是外部类。

虽然this的指向是不同的,但是在lambda表达式中也是可以访问方法的局部变量:

代码语言:javascript
复制
public Runnable createClosureUsingLambda(){
        int count=10;
        Runnable runnable=()-> System.out.println(count);
        return runnable;
    }

上面的例子中,我们在lambda表达式中访问了定义的count变量。

深入理解lambda表达式和函数的局部变量

首先lambda表达式是无状态的,因为lambda表达式的本质是函数,它的作用就是在给定输入参数的情况下,输出固定的结果。

如果lambda表达式中引用的方法中的局部变量,则lambda表达式就变成了闭包,因为这个时候lambda表达式是有状态的。我们接下来用个例子来具体说明。

上面的lambda表达式创建的Runnable,我们可以这样使用:

代码语言:javascript
复制
public void testClosureLambda(){
        Runnable runnable=createClosureUsingLambda();
        runnable.run();
    }

为了深入理解lambda表达式和局部变量传值的关系,我们将编译好的class文件进行反编译。

代码语言:javascript
复制
javap -c -p ClosureUsage

将部分输出结果列出如下:

上面我们列出了createClosureUsingLambda和它内部的lambda表达式的反编译结果。

可以看到在createClosureUsingLambda方法中,我们首先定义了一个值为10的int,并将其入栈。

再看lambda表达式生成的方法,我们可以看到这个方法多出了一个int参数,并且通过getstatic命令将参数传递进来。

这就是lambda表达式传递状态的原理。

总结

本文介绍了闭包和lambda表达式之间的关系,并从字节码的角度进一步说明了局部变量是怎么传递给函数内部的lambda表达式的。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-06-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序那些事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档