JavaSE笔记——函数式编程(Lambda表达式)

news2024/11/20 9:12:36

文章目录

  • 前言
  • 一、第一个Lambda表达式
  • 二、如何辨别Lambda表达式
  • 三、引用值,而不是变量
  • 四、函数接口
  • 五、类型推断
  • 总结


前言

Java 8 的最大变化是引入了 Lambda 表达式——一种紧凑的、传递行为的方式。


一、第一个Lambda表达式

Swing 是一个与平台无关的 Java 类库,用来编写图形用户界面(GUI)。该类库有一个常见用法:为了响应用户操作,需要注册一个事件监听器。用户一输入,监听器就会执行一些操作:

Button button = new Button();
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("button clicked");
    }
});

设计匿名内部类的目的,就是为了方便 Java 程序员将代码作为数据传递。不过,匿名内部类还是不够简便。为了调用一行重要的逻辑代码,不得不加上 4 行冗繁的样板代码。这些代码还相当难读,因为它没有清楚地表达程序员的意图。我们不想传入对象,只想传入行为。在 Java 8 中,上述代码可以写成一个
Lambda 表达式,如下所示。

Button button = new Button();
button.addActionListener(e -> System.out.println("button clicked"));

和传入一个实现某接口的对象不同,我们传入了一段代码块——一个没有名字的函数。event 是参数名,和上面匿名内部类示例中的是同一个参数。-> 将参数和 Lambda 表达式的主体分开,而主体是用户点击按钮时会运行的一些代码。

和使用匿名内部类的另一处不同在于声明 event 参数的方式。使用匿名内部类时需要显式地声明参数类型 ActionEvent event,而在 Lambda 表达式中无需指定类型,程序依然可以编译。这是因为 javac 根据程序的上下文(addActionListener 方法的签名)在后台推断出了参数 event 的类型。这意味着如果参数类型不言而明,则无需显式指定。

二、如何辨别Lambda表达式

Lambda 表达式除了基本的形式之外,还有几种变体,如下所示:

Runnable runnable = () -> System.out.println("hello world"); // 1
ActionListener actionListener = e -> System.out.println("button clicked");// 2
Runnable runnable1 = () ->{
    System.out.print("Hello");
    System.out.println(" World");
}; // 3
BinaryOperator<Long> add = (x, y) -> x + y; // 4
BinaryOperator<Long> addExplicit = (Long x, Long y) -> x + y; // 5
  1. 中所示的 Lambda 表达式不包含参数,使用空括号 () 表示没有参数。该 Lambda 表达式实现了 Runnable 接口,该接口也只有一个 run 方法,没有参数,且返回类型为 void。
  2. 中所示的 Lambda 表达式包含且只包含一个参数,可省略参数的括号。
  3. Lambda 表达式的主体不仅可以是一个表达式,而且也可以是一段代码块,使用大括号({})将代码块括起来,该代码块和普通方法遵循的规则别无二致,可以用返回或抛出异常来退出。只有一行代码的 Lambda 表达式也可使用大括号,用以明确 Lambda表达式从何处开始、到哪里结束。
  4. Lambda 表达式也可以表示包含多个参数的方法,这行代码并不是将两个数字相加,而是创建了一个函数,用来计算两个数字相加的结果。变量 add 的类型是 BinaryOperator<Long>,它不是两个数字的和,而是将两个数字相加的那行代码。
  5. 到目前为止,所有 Lambda 表达式中的参数类型都是由编译器推断得出的。这当然不错,但有时最好也可以显式声明参数类型,此时就需要使用小括号将参数括起来,多个参数的情况也是如此。

三、引用值,而不是变量

如果你曾使用过匿名内部类,也许遇到过这样的情况:需要引用它所在方法里的变量。这时,需要将变量声明为 final。将变量声明为 final,意味着不能为其重复赋值。同时也意味着在使用 final 变量时,实际上是在使用赋给该变量的一个特定的值。

String name = "saa";
Button button = new Button();
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("button clicked");
        System.out.println(name);
    }
});

Java 8 虽然放松了这一限制,可以引用非 final 变量,但是该变量在既成事实上必须是final。虽然无需将变量声明为 final,但在 Lambda 表达式中,也无法用作非终态变量。既成事实上的 final 是指只能给该变量赋值一次。换句话说,Lambda 表达式引用的是值,而不是变量。如果你试图给该变量多次赋值,然后在 Lambda 表达式中引用它,编译器就会报错。
在这里插入图片描述
这种行为也解释了为什么 Lambda 表达式也被称为闭包。未赋值的变量与周边环境隔离起来,进而被绑定到一个特定的值。在众说纷纭的计算机编程语言圈子里,Java 是否拥有真正的闭包一直备受争议,因为在 Java 中只能引用既成事实上的 final 变量。名字虽异,功能相同。

四、函数接口

函数接口是只有一个抽象方法的接口,用作 Lambda 表达式的类型。

使用只有一个方法的接口来表示某特定方法并反复使用,是很早就有的习惯。,Lambda 表达式也使用同样的技巧,并将这种接口称为函数接口。如下展示了前面例子中所用的函数接口:

public interface ActionListener extends EventListener {
    public void actionPerformed(ActionEvent e);
}

ActionListener 只有一个抽象方法:actionPerformed,被用来表示行为:接受一个参数,返回空。记住,由于 actionPerformed 定义在一个接口里,因此 abstract 关键字不是必需的。该接口也继承自一个不具有任何方法的父接口:EventListener。

这就是函数接口,接口中单一方法的命名并不重要,只要方法签名和 Lambda 表达式的类型匹配即可。可在函数接口中为参数起一个有意义的名字,增加代码易读性,便于更透彻地理解参数的用途。

常用的函数式接口:
在这里插入图片描述
在这里插入图片描述

五、类型推断

某些情况下,用户需要手动指明类型,建议大家根据自己或项目组的习惯,采用让代码最便于阅读的方法。有时省略类型信息可以减少干扰,更易弄清状况;而有时却需要类型信息帮助理解代码。经验证发现,一开始类型信息是有用的,但随后可以只在真正需要时才加上类型信息。

Lambda 表达式中的类型推断,实际上是 Java 7 就引入的目标类型推断的扩展。我们已经知道 Java 7 中的菱形操作符,它可使 javac 推断出泛型参数的类型。

Map<String, Integer> oldWordCounts = new HashMap<String, Integer>(); // 1
Map<String, Integer> diamondWordCounts = new HashMap<>(); // 2

我们为变量 oldWordCounts 1明确指定了泛型的类型,而变量 diamondWordCounts 2则使用了菱形操作符。不用明确声明泛型类型,编译器就可以自己推断出来。

如果将构造函数直接传递给一个方法,也可根据方法签名来推断类型。在下面例子中,如果将构造函数直接传递给一个方法,也可根据方法签名来推断类型。

useHashmap(new HashMap<>());
private void useHashmap(Map<String, String> values);

Java 7 中程序员可省略构造函数的泛型类型,Java 8 更进一步,程序员可省略 Lambda 表达式中的所有参数类型。javac 根据 Lambda 表达式上下文信息就能推断出参数的正确类型。程序依然要经过类型检查来保证运行的安全性,但不用再显式声明类型罢了。这就是所谓的类型推断。


总结

Lambda 表达式是一个匿名方法,将行为像数据一样进行传递。Lambda 表达式的常见结构:BinaryOperator<Integer> add = (x, y) → x + y。函数接口指仅具有单个抽象方法的接口,用来表示 Lambda 表达式的类型。

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

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

相关文章

数组与集合有什么不同之处

这个问题其实就是一个非常基础的面试题&#xff0c;一般面试官想了解你基础知识方面的掌握时&#xff0c;基本都会问这个问题&#xff0c;尤其是一些&#xff0c;计算机学子毕业之后&#xff0c;如果还是想要从事计算机技术相关的行业时&#xff0c;那么在面试的时候就需要做好…

Faster RCNN网络源码解读(Ⅰ) --- Fast RCNN、Faster RCNN论文解读

目录 一、Fast R-CNN论文解读 二、Faster R-CNN论文解读 一、Fast R-CNN论文解读 Fast R-CNN是作者Ross Girshick继R-CNN后的又一力作。同样使用VGG16作为网络的backbone&#xff0c;与R-CNN相比训练时间快9倍&#xff0c;测试推理时间快213倍&#xff0c;准确率从62%提升至66…

Win10家庭版安装Docker桌面版

文章目录1.主板BIOS中开启虚拟化2.开启Hyper-V&#xff08;1&#xff09;使用下列代码生成一个.cmd文件&#xff0c;并以管理员身份运行该文件。&#xff08;2&#xff09;看到运行成功即可关闭该文件。&#xff08;3&#xff09;勾选Hyper-V设置里的所有选项&#xff08;4&…

2-2-3-10、并发设计模式

目录终止线程的设计模式Two-phase Termination&#xff08;两阶段终止&#xff09;模式——优雅的终止线程使用场景避免共享的设计模式Immutability模式——想破坏也破坏不了如何实现使用 Immutability 模式的注意事项Copy-on-Write模式应用场景Thread-Specific Storage 模式—…

这几个实用的微信功能,你该知道

生活中我们每天都会使用到微信&#xff0c;用了10年才发现&#xff0c;原来微信隐藏这么多神功能&#xff0c;真是太实用了&#xff0c;下面一起来看看吧&#xff01;朋友圈发长视频 如果你想要在朋友圈里分享长视频&#xff0c;可以借助微信收藏“转发”到朋友圈。 在收藏里添…

一位普通前端开发的一年|2022总结

前言 2022年转眼快要结束&#xff0c;在这一年我学到了很多东西&#xff0c;现在就来总结一下我的2022&#xff0c;复盘一下过去一年的成果。 总结 在2022年2月底从上一家公司跳槽&#xff0c;面试了两家公司两家拿到了两个offer&#xff0c;当时感觉自己很厉害&#xff0c;…

jdk-Atomic源码学习

上文&#xff1a;AQS-Exchanger源码学习源码下载&#xff1a;https://gitee.com/hong99/jdk8Atomic了解atomic是并发框架中的一员&#xff0c;所属位置&#xff1a;java.util.concurrent.atomic 该类主要是用来解决内存可见性、有序、线程安全&#xff0c;当然底层也是通过cas来…

NEUQ week8 动态规划

题目 P1616 疯狂的采药 题目背景 此题为纪念 LiYuxiang 而生。 题目描述 LiYuxiang 是个天资聪颖的孩子&#xff0c;他的梦想是成为世界上最伟大的医师。为此&#xff0c;他想拜附近最有威望的医师为师。医师为了判断他的资质&#xff0c;给他出了一个难题。医师把他带到一个…

贪吃蛇基础·框架版(简单)结尾有原码

更新不易&#xff0c;麻烦多多点赞&#xff0c;欢迎你的提问&#xff0c;感谢你的转发&#xff0c; 最后的最后&#xff0c;关注我&#xff0c;关注我&#xff0c;关注我&#xff0c;你会看到更多有趣的博客哦&#xff01;&#xff01;&#xff01; 喵喵喵&#xff0c;你对我…

PSM倾向得分匹配代码和案例数据

PSM倾向得分匹配代码和案例数据 含义&#xff1a;倾向评分匹配&#xff08;Propensity Score Matching&#xff0c;简称PSM&#xff09;是一种统计学方法&#xff0c;用于处理观察研究&#xff08;Observational Study&#xff09;的数据。 在观察研究中&#xff0c;由于种种…

【迅为iMX6Q】开发板 u-boot 2020.04 SD卡 启动

前言 iMX6Q 支持多种启动方式&#xff0c;如 emmc启动、SD 卡启动等&#xff0c;这里简单的记录一下 SD卡启动的流程 下载u-boot 使用 NXP 官方提供的 uboot-imx&#xff0c;代码地址为&#xff1a; https://github.com/nxp-imx/uboot-imx 使用 git 下载 uboot-imx&#xff…

自动驾驶接力赛,还在继续奔跑的Apollo带来哪些改变?

自动驾驶&#xff0c;被看作人工智能技术落地最快、范围最广、引发商业效应最显著的场景之一。从人人好奇的无人车&#xff0c;到满大街司空见惯的无人配送&#xff0c;自动驾驶在过去几年里快速成熟&#xff0c;成为城市交通系统的重要组成部分。数据显示&#xff0c;2020-203…

融云 CEO 董晗:国产化进程加速,助推政企数智办公平台深化发展

完整报告关注公众号&#xff0c;限免下载 政策催化加疫情助推下&#xff0c;办公线上化迅速完成着市场教育已经成为当前的主流趋势。而随着“国产化”成为各行业数字化发展道路上的关键词&#xff0c;政企办公领域国产替代的发展确定性更加凸显。关注【融云 RongCloud】&#x…

hnu计网实验四-网络层与链路层协议分析(PacketTracer)

一、实验目的&#xff1a; 通过本实验&#xff0c;进一步熟悉PacketTracer的使用&#xff0c;学习路由器与交换机的基本配置&#xff0c;加深对网络层与链路层协议的理解。 二、实验内容&#xff1a; 4.1 路由器交换机的基本配置 打开下面的实验文件&#xff0c;按提示完成实…

Java 集合框架

目录 1. 简介 2. 概览 3. 实现类 3.1 List 3.2 Queue 3.3 Set 3.4 Map 1. 简介 容器&#xff0c;就是可以容纳其他Java对象的对象。*Java Collections Framework(JCF)*为Java开发者提供了通用的容器&#xff0c;其始于JDK 1.2&#xff0c;优点是: 降低编程难度提高程序…

成为用友ISV优选伙伴,迈丹科技8个月经历了一次脱胎换骨

12月9日&#xff0c;用友第四届企业云服务开发者大赛鸣金收兵&#xff0c;第一次参赛&#xff0c;第一次与用友正式合作的厦门市迈丹科技开发有限公司&#xff08;以下简称迈丹科技&#xff09;&#xff0c;便凭借SPC企业质量管理解决方案第一次获奖&#xff0c;而且还是企业赛…

Unity模型

1、Unity中使用的模型 Unity支持很多模型格式&#xff0c;比如&#xff1a;.fbx&#xff0c;.dae&#xff0c;.3ds&#xff0c;.dxf&#xff0c;.obj等等 大部分模型都不是在Unity中制作的&#xff0c;都是美术人员在建模软件中制作&#xff0c;如 3DMax、Maya等等 当他们制作…

VueJs中的ref函数

前言在Vue2.0里面,与页面相关显示的数据是挂载在data下,而在vue3.0里,想要一个数据具备响应式,那么需要引入响应式API函数,通过API函数加工处理后,才具备响应式,两者之间在底层实现数据的响应式上也存在着差异01ref可将数据变成响应式在setup里面使用let定义的变量不是响应式的…

【XR】VR手柄定位技术

rvy competitive analyse Y1的手柄使用了基于红外光的主动式光学定位追踪方案&#xff0c;主要是通过头显的摄像头在短曝光图像上观测到手柄上红外光电发射的红外光&#xff0c;通过检测光点并根据多视图几何原理计算初步的位姿&#xff0c;然后融合产生于手柄IMU&#xff08;…

深度优先搜索和广度优先搜索的java代码实现过程详解

深度优先搜索和广度优先搜索 在很多情况下&#xff0c;我们需要遍历图&#xff0c;得到图的一些性质&#xff0c;例如&#xff0c;找出图中与指定的顶点相连的所有顶点&#xff0c;或者判定某个顶点与指定顶点是否相通&#xff0c;是非常常见的需求。 有关图的搜索&#xff0c;…