文章目录

  • 简介
  • JS中的闭包
  • java中的闭包
  • 深入理解lambda表达式和函数的局部变量
  • 总结

简介

我们通常讲到闭包,一般都是指在javascript的环境中。闭包是JS中一个非常重要的也非常常用的概念。闭包产生的原因就是变量的作用域范围不同。一般来说函数内部的定义的变量只有函数内部可见。如果我们想要在函数外部操作这个变量就需要用到闭包了。

更多精彩内容且看:

  • 区块链从入门到放弃系列教程-涵盖密码学,超级账本,以太坊,Libra,比特币等持续更新
  • Spring Boot 2.X系列教程:七天从无到有掌握Spring Boot-持续更新
  • Spring 5.X系列教程:满足你对Spring5的一切想象-持续更新
  • java程序员从小工到专家成神之路(2020版)-持续更新中,附详细文章教程

更多内容请访问www.flydean.com

JS中的闭包

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

  function f1(){var n=10;}
  alert(n); // error

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

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

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中是没有函数的概念的。和函数差不多相当的就是方法了。

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

public Runnable createClosureUsingClass(){int count=10;Runnable runnable= new Runnable() {@Overridepublic void run() {System.out.println(count);}};return runnable;}

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

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

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

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

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

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

public Runnable createClosureUsingLambda(){int count=10;Runnable runnable=()-> System.out.println(count);return runnable;}

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

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

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

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

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

public void testClosureLambda(){Runnable runnable=createClosureUsingLambda();runnable.run();}

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

javap -c -p ClosureUsage

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

  public java.lang.Runnable createClosureUsingLambda();Code:0: bipush        102: istore_13: iload_14: invokedynamic #12,  0             // InvokeDynamic #0:run:(I)Ljava/lang/invokedynamicinvokedynamic;9: astore_210: aload_211: areturnprivate static void lambda$createClosureUsingLambda$0(int);Code:0: getstatic     #29                 // Field java/lang/System.out:Ljava/io/PrintStream;3: iload_04: invokevirtual #35                 // Method java/io/PrintStream.println:(I)V7: return

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

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

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

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

总结

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

本文的例子https://github.com/ddean2009/
learn-java-base-9-to-20

本文作者:flydean程序那些事

本文链接:http://www.flydean.com/java-lambda-closure/

本文来源:flydean的博客

欢迎关注我的公众号:程序那些事,更多精彩等着您!

Lambda表达式和闭包Closure相关推荐

  1. C# 从CIL代码了解委托,匿名方法,Lambda 表达式和闭包本质

    前言 C# 3.0 引入了 Lambda 表达式,程序员们很快就开始习惯并爱上这种简洁并极具表达力的函数式编程特性. 本着知其然,还要知其所以然的学习态度,笔者不禁想到了几个问题. (1)匿名函数(匿 ...

  2. java闭包和lambda关系_Lambda表达式和闭包Closure

    简介 我们通常讲到闭包,一般都是指在javascript的环境中.闭包是JS中一个非常重要的也非常常用的概念.闭包产生的原因就是变量的作用域范围不同.一般来说函数内部的定义的变量只有函数内部可见.如果 ...

  3. python3 入门 (三) 函数与lambda表达式、闭包

    函数 是组织好的.可重复使用的.用来实现单一或相关联功能的代码段. 函数代码块以def关键词开头,后接函数标识符名称和圆括号() 任何传入参数和自变量必须放在圆括号中间.圆括号之间可以用于定义参数 函 ...

  4. 深入理解Java Lambda表达式,匿名函数,闭包

    前言 对于Lambda表达式一直是知其然不知其所以然,为了搞清楚什么是Lambda表达式,以及Lambda表达式的用法和作用,本文应运而生当做学习笔记分享出来,欢迎指正交流. 什么是Lambda 让我 ...

  5. C++11 lambda表达式与函数对象

    C++ lambda表达式与函数对象 lambda表达式是C++11中引入的一项新技术,利用lambda表达式可以编写内嵌的匿名函数,用以替换独立函数或者函数对象,并且使代码更可读.但是从本质上来讲, ...

  6. Java 核心技术卷1 --第六章 接口、lambda表达式和内部类

    吧Github代码链接: https://github.com/deyou123/corejava.git 第六章 接口.lambda表达式和内部类 6.1 接口 6.1.1 接口概念 接口不是类,而 ...

  7. CoreJava 笔记总结-第六章 接口、lambda表达式与内部类

    文章目录 第六章 接口.lambda表达式与内部类 ==接口== 接口的概念 接口的属性 接口与抽象类 静态和私有方法 默认方法 解决默认方法冲突 接口与回调 `Comparator`接口 对象克隆 ...

  8. lambda表达式浅析【C++学习笔记】

    lambda表达式浅析[C++学习笔记] 基本用法: auto f = [/*捕获列表*/](/*参数*/)->int /*后置返回值类型*/{/** 函数体*/}; 捕获列表: [] : 不捕 ...

  9. 【java8新特性】——lambda表达式与函数式接口详解(一)

    一.简介 java8于2014年发布,相比于java7,java8新增了非常多的特性,如lambda表达式.函数式接口.方法引用.默认方法.新工具(编译工具).Stream API.Date Time ...

最新文章

  1. Selenium 爬虫时遇到的问题 Selenium message:session not created
  2. Linux 监视磁盘空间和使用情况
  3. 常用网络故障集锦,收藏备用
  4. Excel电子表格输入技巧大比拼
  5. Asp.Net上传组件
  6. 利用SecureCRT在linux与Windows之间传输文件
  7. 第一阶段 07类与对象
  8. Win11语音助手怎么开启 Win11语音助手开启的方法
  9. ubuntu 14.04 编译yocto源码--环境配置篇
  10. XHTML中button加入超链接以及使插入图片与屏幕一样大
  11. excel导出 服务器运行失败,SolidWorks 插入自制EXCEL明细表 启动服务器应用程序失败:启动excle服务器失败...
  12. 我的 Hadoop 3.2.2 之旅 【收藏夹吃灰系列】
  13. 3dmax2014卸载/安装失败/如何彻底卸载清除干净3dmax2014注册表和文件的方法
  14. (转载)NPOI使用手册,实践发现使用2.2版本的库需要稍作调整
  15. JAVA Applet——绘制心形曲线
  16. 计算机音乐数字谱抖音,抖音计算器音乐乐谱
  17. javaScript和html的区别与联系
  18. imx6ul查看系统资源IO电平(基于周立功A6G2C)
  19. mysql创建储存过程 输入学生名子_创建一个存储过程,给定某学生学号,要求查询出该学生的姓名,所选课程名和成绩.(SQL SERVER)...
  20. 三角形中的正方形,三个问题

热门文章

  1. 2021已去,2022未来
  2. 滚动图片广告_张韶涵霸屏兴发广场,户外LED大屏广告:投放价值在哪?
  3. 迭代加深搜索与埃及分数求解
  4. 斯特林反演[bzoj4671]异或图
  5. Burpsuite中protobuf数据流的解析 - Vincent
  6. 对现有的所能找到的DDOS代码(攻击模块)做出一次分析----SYN(洪水攻击)篇
  7. WinSock三种选择I/O模型
  8. 用Python实现队列
  9. RabbitMQ基础概念详解
  10. Go gomaxprocs 调高引起调度性能损耗