Lambda表达式和闭包Closure

2024-02-24 16:48

本文主要是介绍Lambda表达式和闭包Closure,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 简介
  • 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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/742739

相关文章

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

如何掌握面向对象编程的四大特性、Lambda 表达式及 I/O 流:全面指南

这里写目录标题 OOP语言的四大特性lambda输入/输出流(I/O流) OOP语言的四大特性 面向对象编程(OOP)是一种编程范式,它通过使用“对象”来组织代码。OOP 的四大特性是封装、继承、多态和抽象。这些特性帮助程序员更好地管理复杂的代码,使程序更易于理解和维护。 类-》实体的抽象类型 实体(属性,行为) -》 ADT(abstract data type) 属性-》成

Java基础回顾系列-第三天-Lambda表达式

Java基础回顾系列-第三天-Lambda表达式 Lambda表达式方法引用引用静态方法引用实例化对象的方法引用特定类型的方法引用构造方法 内建函数式接口Function基础接口DoubleToIntFunction 类型转换接口Consumer消费型函数式接口Supplier供给型函数式接口Predicate断言型函数式接口 Stream API 该篇博文需重点了解:内建函数式

C语言程序设计(数据类型、运算符与表达式)

一、C的数据类型 C语言提供的数据类型: 二、常量和变量 2.1常量和符号常量 在程序运行过程中,其值不能被改变的量称为常量。 常量区分为不同的类型: 程序中用#define(预处理器指令)命令行定义变量将代表常量,用一个标识符代表一个常量,称为符合常量。 2.2变量 变量代表内存中具有特定属性的一个存储单元,用来存放数据,在程序运行期间,这些值是可以 改变的。 变

JavaSE(十三)——函数式编程(Lambda表达式、方法引用、Stream流)

函数式编程 函数式编程 是 Java 8 引入的一个重要特性,它允许开发者以函数作为一等公民(first-class citizens)的方式编程,即函数可以作为参数传递给其他函数,也可以作为返回值。 这极大地提高了代码的可读性、可维护性和复用性。函数式编程的核心概念包括高阶函数、Lambda 表达式、函数式接口、流(Streams)和 Optional 类等。 函数式编程的核心是Lambda

逻辑表达式,最小项

目录 得到此图的逻辑电路 1.画出它的真值表 2.根据真值表写出逻辑式 3.画逻辑图 逻辑函数的表示 逻辑表达式 最小项 定义 基本性质 最小项编号 最小项表达式   得到此图的逻辑电路 1.画出它的真值表 这是同或的逻辑式。 2.根据真值表写出逻辑式   3.画逻辑图   有两种画法,1是根据运算优先级非>与>或得到,第二种是采

将浮点型算式的中缀表达式转换成后缀表达式并算出式子结果

最近因为需要了解如何将在Win应用程序控制台输入的算式表达式转化成其后缀表达式的算法,所以在网上搜索了一下,看到许多人的程序都只是对应于运算数在0~9的范围内的整型运算式,所以自己就写了一个可以计算浮点型算式的程序,一下是运行时的截图: 式子中的a,b,c是可供用户自行输入的变量。 首先,我先对输入的运算符进行了简单的合法性判断,我的判断代 码如下: //函数的传入参

JavaScript深入理解闭包

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。 一、变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域。 变量的作用域无非就是两种:全局变量和局部变量。 Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。 Js代码   var n=999;   function f1