【lambda表达式】变量作用域和lambda 表达式的处理

news2024/10/6 18:51:41

变量作用域

通常, 你可能希望能够在 lambda 表达式中访问外围方法或类中的变量。

public static void repeatMessage(String text, int delay){
    ActionListener listener = event ->{ 
    	System.out.println(text);
        Toolkit.getDefaultToolkitO.beep();
	}
	new Timer(delay, listener).start0;
}

注意 lambda 表达式中的变量text并不是在这个 lambda 表达式中定义的。实际上,这是 repeatMessage 方法的一个参数变量。

想想看, 这里好像会有问题, 尽管不那么明显。lambda 表达式的代码可能会在 repeatMessage 调用返回很久以后才运行,而那时这个参数变量已经不存在了。如何保留 text 变量呢?

要了解到底会发生什么,下面来巩固我们对 lambda 表达式的理解。lambda 表达式有 3 个部分:

  1. 一个代码块;
  2. 参数;
  3. 自由变量的值, 这是指非参数而且不在代码中定义的变量。

在例子中, 这个 lambda 表达式有 1 个自由变量 text。 表示 lambda 表达式的数据结构必须存储自由变量的值,在这里就是字符串 “Hello” 。我们说它被 lambda 表达式捕获 (下面来看具体的实现细节。 例如,可以把一个 lambda 表达式转换为包含一个方法的对象,这样自由变量的值就会复制到这个对象的实例变量中。)

关于代码块以及自由变量值有一个术语: 闭包(closure) 。如果有人吹嘘他们的语言有闭包,现在你也可以自信地说 Java 也有闭包。在 Java 中, lambda 表达式就是闭包。

可以看到,lambda 表达式可以捕获外围作用域中变量的值。 在 Java 中,要确保所捕获的值是明确定义的,这里有一个重要的限制。在 lambda 表达式中, 只能引用值不会改变的变量。

之所以有这个限制是有原因的。如果在 lambda 表达式中改变变量, 并发执行多个动作时就会不安全。对于目前为止我们看到的动作不会发生这种情况,不过一般来讲,这确实是 一个严重的问题。 另外如果在 lambda 表达式中引用变量, 而这个变量可能在外部改变,这也是不合法的。

这里有一条规则:lambda 表达式中捕获的变量必须实际上是最终变量 ( effectively final)。 实际上的最终变量是指, 这个变量初始化之后就不会再为它赋新值。在这里,text 总是指示 同一个 String 对象,所以捕获这个变量是合法的。

lambda 表达式的体与嵌套块有相同的作用域。这里同样适用命名冲突和遮蔽的有关规则。在 lambda 表达式中声明与一个局部变量同名的参数或局部变量是不合法的。

public class Application{
	public void init(){
		ActionListener listener = event ->{
			System.out.println(this.toString());
            ...
        }
        ...
    }
}

表达式 this.toString() 会调用 Application 对象的 toString方法, 而不是 ActionListener 实例的方法。在 lambda 表达式中, this 的使用并没有任何特殊之处。lambda 表达式的作用域嵌套在 init 方法中,与出现在这个方法中的其他位置一样, lambda 表达式中 this 的含义并没有变化。

lambda 表达式的处理

到目前为止, 你已经了解了如何生成 lambda 表达式, 以及如何把 lambda 表达式传递到 需要一个函数式接口的方法。下面来看如何编写方法处理 lambda 表达式。

使用 lambda 表达式的重点是延迟执行 ( deferred execution )。 毕竟, 如果想要立即执行代码,完全可以直接执行, 而无需把它包装在一个lambda 表达式中。之所以希望以后再执行代码, 这有很多原因, 如:

  • 在一个单独的线程中运行代码;
  • 多次运行代码;
  • 在算法的适当位置运行代码(例如,排序中的比较操作;)
  • 发生某种情况时执行代码(如,点击了一个按钮,数据到达,等等;)
  • 只在必要时才运行代码。

下面来看一个简单的例子。假设你想要重复一个动作 n 次。 将这个动作和重复次数传递到一个 repeat 方法:

repeat(10, 0 -> System.out.println("Hello, World!")) ;

要接受这个 lambda 表达式, 需要选择(偶尔可能需要提供)一个函数式接口。 表 6-1 列出了 Java API 中提供的最重要的函数式接口。在这里, 我们可以使用 Runnable 接口:

public static void repeat(int n, Runnable action) { for (int i = 0; i < n; i++) action.run(); }

需要说明的是,调用 action.run() 时会执行这个 lambda 表达式的主体。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uzeboIQ8-1669815253331)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9a334dad116545f49d18e964b2f64950~tplv-k3u1fbpfcp-zoom-1.image)]

现在让这个例子更复杂一些。我们希望告诉这个动作它出现在哪一次迭代中。 为此,需要选择一个合适的函数式接口,其中要包含一个方法, 这个方法有一个 int 参数而且返回类型为 void。处理 int 值的标准接口如下:

public interface IntConsumer{
	void accept(int value);
}

下面给出 repeat 方法的改进版本:

public static void repeat(int n, IntConsumer action){
	for (int i = 0; i < n; i++) action.accept(i);
}

可以如下调用它:

repeat(10, i -> System.out.println(" Countdown: " + (9 - i)));

表 6-2 列出了基本类型 int 、long 和 double 的 34 个可能的规范。 最好使用这些特殊化规范来减少自动装箱。出于这个原因, 我在上一节的例子中使用了 IntConsumer 而不是 Consume<lnteger>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rxFQHhNb-1669815253332)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6deeb1291f0e4bafb8aa03fb9eebe902~tplv-k3u1fbpfcp-zoom-1.image)]

最好使用表 6-1 或表 6-2 中的接口。 例如,假设要编写一个方法来处理满足某个特定条件的文件。 对此有一个遗留接口 java.io.FileFilter, 不过最好使用标准的 Predicate , 只有一种情况下可以不这么做, 那就是你已经有很多有用的方法可以生成 FileFilter 实例。

大多数标准函数式接口都提供了非抽象方法来生成或合并函数。 例如,Predicate. isEqual(a)等同于 a::equals, 不过如果 a 为 null 也能正常工作。已经提供了默认方法 and 、or 和 negate 来合并谓词。 例如, Predicate.isEqual(a).or(Predicate.isEqual(b)) 就等同于 x -> a.equals(x) || b.equals(x)

如果设计你自己的接口,其中只有一个抽象方法,可以用 @FunctionalInterface 注解来标记这个接口。这样做有两个优点。 如果你无意中增加了另一个非抽象方法, 编译器会产生一个错误消息。 另外 javadoc 页里会指出你的接口是一个函数式接口。

并不是必须使用注解根据定义,任何有一个抽象方法的接口都是函数式接口。不过使用 @FunctionalInterface 注解确实是一个很好的做法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/52154.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PowerShell 打开十六进制文件

1&#xff0c;打开PowerShell 2&#xff0c;进入文件所在路径 3&#xff0c;Format-Hex -Path ./bootloader.bin 4&#xff0c;效果

selenium UI使用小技巧集合

selenium这个工具就不用我多介绍了吧&#xff0c;咱们已经说过很多很多次咯&#xff0c;所以就直接上主题&#xff1a; 窗口截图 webdriver 提供了 get_screenshot_as_file()函数来截取当前窗口 from selenium import webdriver from time import sleepdriver webdriver.Ch…

超详细的Python实现MySQL数据库基本操作,今天小编给大家整理好了

一、SQL语句 (mysql 数据库中的语言) show databases;查看数据库 use "database_ name" ;进入数据库 show tables; 查看当前数据库中有哪些表 select * from "table_ name";查询数据表中的所有内容 describe "table_ name"; 查看表结构 desc &q…

外包干了2年,彻底废了...

先说一下自己的情况。大专生&#xff0c;17年通过校招进入湖南某软件公司&#xff0c;干了接近2年的点点点&#xff0c;今年年上旬&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落&#xff01;而我已经在一个企业干了五年的功能测试…

【Linux】Linux下基本指令(一)

作者&#xff1a;一个喜欢猫咪的的程序员 专栏&#xff1a;《Linux》 喜欢的话&#xff1a;世间因为少年的挺身而出&#xff0c;而更加瑰丽。 ——《人民日报》 目录 一、浅谈操作系统&#xff1a; 1.1什么是操作系统&#xff1f;&#xff…

力扣46:全排列(Java回溯)

一、题目描述 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]示例 2&#xff1a; 输入&…

[附源码]计算机毕业设计springboot汽配管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

python 如何根据索引快速删除列表中的多个元素

一、批量删除列表中不同位置的元素 列表是python中经常用到的一种数据结构&#xff0c;因python提供了很多方法对其增、删、查、改&#xff0c;故使用起来比较灵活&#xff0c;下面就介绍下如何快速删除列表中多个元素的方法。 二、具体用法 例如&#xff1a;待处理列表为[‘…

实体-联系模型--E-R图

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 收录于专栏 数据库干货铺 ⭐E-R图⭐ 文章目录⭐E-R图⭐一&#xff0c;E-R模型的基本概念&#x1f468;‍&#x1f4bb;概…

setCharacterEncoding和setContentType有什么不同

如果仅仅从服务器的角度来看&#xff0c;这两个方法其实本质是一样的&#xff0c;以下内容是摘抄自oracle的官网&#xff1a; Defines an object to assist a servlet in sending a response to the client. The servlet container creates a ServletResponse object and passe…

HITCTF2022-WEB2-easypop

WEB easypop 题目给了源码 <?php // php version 7.4.32 class a{protected $a1;private $a2;private $a3;public function __unset($unset) {$this->a2 [];if($this->a3){if($this->a1->{$unset} ! []){$this->a1->{$unset} $this->a2;}}}funct…

Python编程 顺序执行与程序的主入口

作者简介&#xff1a;一名在校计算机学生、每天分享Python的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.顺序执行 1.顺序执行(了解) 2.程序主入口(熟悉) 前言 本章将会讲解P…

网络安全运维工程师(NISP-SO)需要掌握那些知识点

想要学习了解“网络安全运维工程师&#xff08;NSP-SO)”看这个12点就够了。从“掌握核心能力”到如何“应急响应”并提高安全运维的知识水平 第一阶段 先导基础知识内容 学生可掌握的核心能力 1、能够根据企业需求&#xff0c;搭建基于windows服务器的网站 2、能够根据企业…

二、CSS自制浏览器滑动条

一、滑动条 思路&#xff1a;首先我们需要想清楚&#xff0c;大体思路应该是把浏览器默认滑动条隐藏&#xff0c;然后自己手写一个好看的滑动条&#xff0c;主要是做出和浏览器滑动条一样的上下移动的效果出来。 解释&#xff1a;如下图所示&#xff0c;有一个盒子高度是100p…

【论文阅读笔记】A review of the deep learning methods for medical images super resolut

摘要 医疗图像中分辨率的限制来源于&#xff1a;图像采集次数的限制&#xff0c;由于硬件限制导致的低辐射&#xff08;Low irradiation&#xff09;等。 这篇综述应该比较基础&#xff0c;从深度学习 -> 超分网络架构 -> 再到医疗图像超分问题的介绍。对于医疗方向的介绍…

[附源码]JAVA毕业设计高校信息资源共享平台(系统+LW)

[附源码]JAVA毕业设计高校信息资源共享平台&#xff08;系统LW&#xff09; 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目…

3.13 小红书笔记怎样带话题,才能增加曝光?【玩赚小红书】

虽然很多博主都知道在笔记内容最后要带上一个相关话题&#xff0c;但却很少人知道带什么样的话题&#xff0c;如何找到官方话题或热门话题来提高笔记内容的曝光。这一篇文章黄宇风就来讲讲&#xff0c;小红书笔记该如何带话题。 ​ ​ 1、挖掘小红书笔记热门话题 笔记带话题主…

命令行下编译与运行简单的OC程序

学习OC的语法建议还是用普通的编辑器写OC代码&#xff0c;然后在终端命令行下编译与运行。那我们来看一下是如何在命令行下编译一个OC源文件&#xff0c;以及运行编译后的可执行文件的 开发环境 操作系统&#xff1a;macOS Big Sur 终端&#xff1a;iTerm2 Build 3.4.8 clong…

Linux基础知识与实操-篇五:bash使用进阶

通过上篇 篇四:初识bash与配置 的学习&#xff0c;已经基本认识了Linux下bash工具的使用和配置&#xff0c;下面将讲解过多关于bash在其他地方的使用。 终端机的环境设置 stty -a 命令可以得到 目前环境中 所有的 按键列表&#xff0c;其中 ^ 表示的是 ctrl 按键&#xff0c;…

原来电商企业也能运用模型规划设计营销活动

营销推广是电商重要的运营组成。电商平台要发起一场综合性的推广活动&#xff0c;需要明确参与活动的商品范围、促销价格、推广渠道以及如何触达到消费者等。很多营销推广活动规则复杂且不断变化&#xff0c;就需要使用模型来设计&#xff0c;例如邀人砍一刀的“免费提现”、多…