Java8实战-总结9
Java8实战-总结9
- Lambda表达式
- 把Lambda付诸实践:环绕执行模式
- 第1步:记得行为参数化
- 第2步:使用函数式接口来传递行为
- 第3步:执行一个行为
- 第4步:传递Lambda
- 使用函数式接口
- Predicate
- Consumer
- Function
- 原始类型特化
Lambda表达式
把Lambda付诸实践:环绕执行模式
通过一个例子,看看在实践中如何利用Lambda
和行为参数化来让代码更为灵活,更为简洁。资源处理(例如处理文件或数据库)时一个常见的模式就是打开一个资源,做一些处理,然后关闭资源。这个设置和清理阶段总是很类似,并且会围绕着执行处理的那些重要代码。这就是所谓的环绕执行(execute around)模式,如下图所示。例如,在以下代码中,中间部分就是从一个文件中读取一行所需的模板代码(注意使用了Java 7中的带资源的try语句,它已经简化了代码,因为不需要显式地关闭资源了):
public static String processFile() throws IOException {//这就是做有用工作的那行代码try(BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {return br.readLine();}
}
第1步:记得行为参数化
现在这段代码是有局限的。只能读文件的第一行。如果想要返回头两行,甚至是返回使用最频繁的词,该怎么办呢?在理想的情况下,要重用执行设置和清理的代码,并告诉processFile
方法对文件执行不同的操作。这听起来是不是很耳熟?是的,需要把processFile
的行为参数化。需要一种方法把行为传递给processFile
,以便它可以利用BufferedReader
执行不同的行为。
传递行为正是Lambda
的拿手好戏。那要是想一次读两行,这个新的processFile
方法看起来又该是什么样的呢?基本上,需要一个接收BufferedReader
并返回String
的Lambda
。例如,下面就是从BufferedReader
中打印两行的写法:
String result = processFile((BufferedReader br) -> br.readLine() + br.readLine());
第2步:使用函数式接口来传递行为
前面解释过了,Lambda
仅可用于上下文是函数式接口的情况。需要创建一个能匹配BufferedReader -> String
,还可以抛出IOException
异常的接口。把这一接口叫作BufferedReaderProcessor
吧。
@FunctionalInterfacepublic interface BufferedReaderProcessor {String process(BufferedReader b) throws IOException;}
现在就可以把这个接口作为新的processFile
方法的参数了:
public static String processFile(BufferedReaderProcessor p) throws IOException {}
第3步:执行一个行为
任何BufferedReader -> String
形式的Lambda
都可以作为参数来传递,因为它们符合BufferedReaderProcessor
接口中定义的process
方法的签名。现在只需要一种方法在processFile
主体内执行Lambda
所代表的代码。请记住,Lambda
表达式允许直接内联,为函数式接口的抽象方法提供实现,并且将整个表达式作为函数式接口的一个实例。因此,可以在processFile
主体内,对得到的BufferedReaderProcessor
对象调用process
方法执行处理:
public static String processFile(BufferedReaderProcessor p) throws
IOException {try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {//处理BufferedReaderreturn p.process(br);}
}
第4步:传递Lambda
现在你就可以通过传递不同的Lambda重用processFile方法,并以不同的方式处理文件了。处理一行:
String oneLine = processFile((BufferedReader br)-> br.readLine());
处理两行:
String twoLines = processFile((BufferedReader br) -> br.readLine() + br.readLine());
下图总结了所采取的使pocessFile方法更灵活的四个步骤:
使用函数式接口
函数式接口定义且只定义了一个抽象方法。函数式接口很有用,因为抽象方法的签名可以描述Lambda
表达式的签名。函数式接口的抽象方法的签名称为函数描述符。所以为了应用不同的Lambda
表达式,需要一套能够描述常见函数描述符的函数式接口。Java API
中已经有了几个函数式接口,比如Comparable
、Runnable
和Callable
。
Java 8
的库设计师在java.util.function
包中引入了几个新的函数式接口:Predicate
、Consumer
和Function
。
Predicate
java.util.function.Predicate<T>
接口定义了一个名叫test
的抽象方法,它接受泛型T
对象,并返回一个boolean
。这恰恰和先前创建的一样,现在就可以直接使用了。在需要表示一个涉及类型T
的布尔表达式时,就可以使用这个接口。比如,可以定义一个接受String
对象的Lambda
表达式,如下所示:
@FunctionalInterfacepublic interface Predicate<T> {boolean test(T t);}public static <T> List<T> filter(List<T> list, Predicate<T> p) {List<T> results = new ArrayList<>();for(T s : list) {if(p.test(s)) {results.add(s);}}return results;}Predicatec<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();List<String> nonEmpty = filter(listofStrings, nonEmptyStringPredicate);
如果去查Predicate接口的Javadoc
说明,可能会注意到诸如and
和or
等其他方法。现在不用太计较这些。
Consumer
java.util.function.Consumer<T>
定义了一个名叫accept
的抽象方法,它接受泛型T
的对象,没有返回(void
)。如果需要访问类型T
的对象,并对其执行某些操作,就可以使用这个接口。比如,可以用它来创建一个forEach
方法,接受一个Integers
的列表,并对其中每个元素执行操作。在下面的代码中,就可以使用这个forEach
方法,并配合Lambda
来打印列表中的所有元素:
@FunctionalInterface
public interface Consumer<T> {void accept(T t);
}public static <T> void forEach(List<T> list, Consumer<T> c) {for(T i : list){c.accept(i);}
}//Lambda是Consumer中accept方法的实现
forEach(Arrays.asList(1,2,3,4,5),(Integer i) -> System.out.println(i));
Function
java.util.function.Function<T,R>
接口定义了一个叫作apply
的方法,它接受一个泛型T
的对象,并返回一个泛型R
的对象。如果需要定义一个Lambda
,将输入对象的信息映射到输出,就可以使用这个接口(比如提取苹果的重量,或把字符串映射为它的长度)。在下面的代码中,将展示如何利用它来创建一个map
方法,以将一个String
列表映射到包含每个String
长度的Integer
列表。
@FunctionalInterface
public interface Punction<T, R> {R apply(T t);
}public static <T,R> List<R> map(List<T> list, Function<T,R> f) {List<R> result = new ArrayList<>();for(T s : list) {result.add(f.apply(s));}return result;
}//[7,2,6]
//Lambda是Punction接口的apply方法的实现
List<Integer> 1 = map(Arrays.asList("lambdas","in","action"),(String s)-> s.length());
原始类型特化
三个泛型函数式接口:Predicate<T>
、Consumer<T>
和Function<T, R>
。还有些函数式接口专为某些类型而设计。
Java
类型要么是引用类型(比如Byte
、Integer
、Object
、List
),要么是原始类型(比如int
、double
、byte
、char
)。但是泛型(比如Consumer<T>
中的T
)只能绑定到引用类型。这是由泛型内部的实现方式造成的。因此,在Java
里有一个将原始类型转换为对应的引用类型的机制。这个机制叫作装箱(boxing)。相反的操作,也就是将引用类型转换为对应的原始类型,叫作拆箱(unboxing)。Java
还有一个自动装箱机制来帮助程序员执行这一任务:装箱和拆箱操作是自动完成的。比如,这就是为什么下面的代码是有效的(一个int
被装箱成为Integer
):
List<Integer> list = new ArrayList<>();for(int i = 300; i < 400; i++) {list.add(i);}
但这在性能方面是要付出代价的。装箱后的值本质上就是把原始类型包裹起来,并保存在堆里。因此,装箱后的值需要更多的内存,并需要额外的内存搜索来获取被包裹的原始值。
Java 8
为前面所说的函数式接口带来了一个专门的版本,以便在输入和输出都是原始类型时避免自动装箱的操作。比如,在下面的代码中,使用IntPredicate
就避免了对值1000
进行装箱操作,但要是用Predicate<Integer>
就会把参数1000
装箱到一个Integer
对象中:
public interface IntPredicate {boolean test(int t);
}//true(无装箱)
IntPredicate evenNumbers = (int i) -> i % 2 == 0;
evenNumbers.test(1000);//false(装箱)
Predicate<Integer> oddNumbers =(Integer i) -> i % 2 == 1;
oddNumbers.test(1000);
一般来说,针对专门的输入参数类型的函数式接口的名称都要加上对应的原始类型前缀,比如DoublePredicate
、IntConsumer
、LongBinaryoperator
、IntFunction
等。Function
接口还有针对输出参数类型的变种:ToIntFunction<T>
、IntToDoubleFunction
等。
下表总结了Java API
中提供的最常用的函数式接口及其函数描述符。请记得这只是一个起点。如果有需要,可以自己设计一个。请记住,(T, U) -> R
的表达方式展示了应当如何思考一个函数描述符。表的左侧代表了参数类型。这里它代表一个函数,具有两个参数,分别为泛型T
和U
,返回类型为R
。
测验:函数式接口
对于下列函数描述符(即Lambda表达式的签名),请构造一个可以利用这些函数式接口的有效Lambda表达式:(1)T -> R
(2)(int, int) -> int
(3)T -> void
(4)() -> T
(5)(T, U) -> R答案如下。
(1)Function<T,R>不错。它一般用于将类型T的对象转换为类型R的对象(比如Function<Apple, Integer>用来提取苹果的重量)。
(2)IntBinaryOperator具有唯一一个抽象方法,叫作applyAsInt,它代表的函数描述符是(int, int)-> int。
(3) Consumer<T>具有唯一一个抽象方法叫作accept,代表的函数描述符是T -> void。
(4)Supplier<T>具有唯一一个抽象方法叫作get,代表的函数描述符是() -> T。或者,Callable<T>具有唯一一个抽象方法叫作call,代表的函数描述符是() -> T。
(5)BiFunction<T, U, R>具有唯一一个抽象方法叫作apply,代表的函数描述符是(T, U) -> R。
下表总结了一些使用案例、Lambda
的例子,以及可以使用的函数式接口:
异常、Lambda,还有函数式接口又是怎么回事呢?请注意,任何函数式接口都不允许抛出受检异常(checked exception)。如果需要Lambda表达式来抛出异常,有两种办法:定义一个自己的函数式接口,
并声明受检异常,或者把Lambda包在一个try/catch块中。比如,函数式接口BufferedReaderProcessor,它显式声明了一个IOException:
@FunctionalInterface
public interface BufferedReaderProcessor {String process(BufferedReader b) throws IOException;
}
BufferedReaderProcessor p =(BufferedReader br)-> br.readLine();但可能是在使用一个接受函数式接口的API,比如Function<T, R>,没有办法自己创建一个。这种情况下,可以显式捕捉受检异常:
Function<BufferedReader, String> f = (BufferedReader b) -> {try {return b.readLine();}catch(IOException e) {throw new RuntimeException(e);}
};
相关文章:

Java8实战-总结9
Java8实战-总结9 Lambda表达式把Lambda付诸实践:环绕执行模式第1步:记得行为参数化第2步:使用函数式接口来传递行为第3步:执行一个行为第4步:传递Lambda 使用函数式接口PredicateConsumerFunction原始类型特化 Lambda表…...

大数据开发面试必问:Hive调优技巧系列一
Hive必问调优 Hive 调优拆解:Hive SQL 几乎是每一位互联网分析师的必备技能,相信很多小伙伴都有被面试官问到 Hive 优化问题的经历。所以掌握扎实的 HQL 基础尤为重要,hive优化也是小伙伴应该掌握的一项技能,本篇文章具体从hive建表优化、HQ…...

Jupyter Notebook 7重磅发布,新增多个特性!
本文分享Jupyter Notebook大版本v7.0.0更新亮点,及简单测试! 近日,Jupyter Notebook大版本v7.0.0更新,Jupyter Notebook 7基于JupyterLab,因此它包含了过去几年JupyterLab中添加的许多新功能和改进,部分亮…...

linux V4L2子系统——v4l2架构(1)之整体架构
概述 V4L(Video for Linux)是Linux内核中关于视频设备的API接口,涉及视频设备的音频和视频信息采集及处理、视频设备的控制。V4L出现于Linux内核2.1版本,经过修改bug和添加功能,Linux内核2.5版本推出了V4L2(…...

Qt信号与槽机制的本质
引入 对象与对象之间的通信有多个方式,如果我们要提供一种对象之间的通信机制。这种机制,要能够给两个不同对象中的函数建立映射关系,前者被调用时后者也能被自动调用。 再深入一些,两个对象如果都互相不知道对方的存在ÿ…...

Linux:入门学习知识及常见指令
文章目录 入门介绍操作系统的概念Linux机器的使用Linux上的指令 对文件知识的补充文件的定义和一些含义文件和目录的存储绝对路径和相对路径 ls指令pwd指令cd指令touch指令mkdir指令rmdir指令rm指令man指令cp指令mv指令cat指令more指令echo指令输出重定向 less指令find指令grep…...

K8s:Kubernetes 故障排除方法论
写在前面 博文内容为节译整理文中提到的工具大部分是商业软件,不是开源的,作为了解理解不足小伙伴帮忙指正 对每个人而言,真正的职责只有一个:找到自我。然后在心中坚守其一生,全心全意,永不停息。所有其它…...

TCP 三次握手四次挥手浅析
大家都知道传输层中的TCP协议是面向连接的,提供可靠的连接服务,其中最出名的就是三次握手和四次挥手。 一、三次握手 三次握手的交互过程如下 喜欢钻牛角尖的我在学习三次握手的时候就想到了几个问题:为什么三次握手是三次?不是…...

【软件安装】MATLAB_R2021b for mac 安装
Mac matlab_r2021b 安装 下载链接:百度网盘 下载链接中所有文件备用。 我所使用的电脑配置: Macbook Pro M1 Pro 16512 系统 macOS 13.5 安装步骤 前置准备 无此选项者,自行百度 “mac 任何来源”。 1 下载好「MATLAB R2021b」安装文…...

电脑维护:10妙招,让你的电脑更加稳定!
你的电脑已经成为你工作、学习、娱乐的最佳工具之一,但是如果你不做好电脑维护工作,就可能面临着电脑变慢、蓝屏、崩溃等问题。在这篇文章中,我们将介绍10个电脑维护步骤,让你的电脑更加稳定! 为什么需要电脑维护&…...

大数据面试题:Kafka的单播和多播
面试题来源: 《大数据面试题 V4.0》 大数据面试题V3.0,523道题,679页,46w字 参考答案: 1、单播 一条消息只能被某一个消费者消费的模式称为单播。要实现消息单播,只要让这些消费者属于同一个消费者组即…...

python与深度学习(八):CNN和fashion_mnist二
目录 1. 说明2. fashion_mnist的CNN模型测试2.1 导入相关库2.2 加载数据和模型2.3 设置保存图片的路径2.4 加载图片2.5 图片预处理2.6 对图片进行预测2.7 显示图片 3. 完整代码和显示结果4. 多张图片进行测试的完整代码以及结果 1. 说明 本篇文章是对上篇文章训练的模型进行测…...

开发一个RISC-V上的操作系统(五)—— 协作式多任务
目录 往期文章传送门 一、什么是多任务 二、代码实现 三、测试 往期文章传送门 开发一个RISC-V上的操作系统(一)—— 环境搭建_riscv开发环境_Patarw_Li的博客-CSDN博客 开发一个RISC-V上的操作系统(二)—— 系统引导程序&a…...

Mybatis-plus集合
目录 mybatis-plus集合1、简介2、特性3、开始使用4、QueryWrapper的使用5、补充 mybatis-plus集合 1、简介 MyBatis-Plus (简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。 m…...

C++ 结构体和联合体
1.结构体 结构体是一种特殊形态的类,它和类一样,可以有自己的数据成员和函数成员,可以有自己的构造函数和析构函数,可以控制访问权限,可以继承,支持包含多态,结构体定义的语法和类的定义语法几…...

使用TensorFlow训练深度学习模型实战(下)
大家好,本文接TensorFlow训练深度学习模型的上半部分继续进行讲述,下面将介绍有关定义深度学习模型、训练模型和评估模型的内容。 定义深度学习模型 数据准备完成后,下一步是使用TensorFlow搭建神经网络模型,搭建模型有两个选项…...

lucene、solr、es的区别以及应用场景
目录 1. Lucene:2. Solr:3. Elasticsearch: Lucene、Solr 和 Elasticsearch(ES) 都是基于 Lucene 引擎的搜索引擎,它们之间有相似之处,但也有一些不同之处。 Lucene 是一个低级别的搜索引擎库,它提供了一种用于创建和维护全文索引的 API&…...

Java方法的使用(重点:形参和实参的关系、方法重载、递归)
目录 一、Java方法 * 有返回类型,在方法体里就一定要返回相应类型的数据。没有返回类型(void),就不要返回!! * 方法没有声明一说。与C语言不同(C语言是自顶向下读取代码)&#…...

登录页的具体实现 (小兔鲜儿)【Vue3】
登录页 整体认识和路由配置 整体认识 登录页面的主要功能就是表单校验和登录登出业务 准备模板 <script setup></script><template><div><header class"login-header"><div class"container m-top-20"><h1 cl…...

大学如何自学嵌入式开发?
1. C语言:C语言是基础中的基础,刚开始学习不用太深入,一本常用的C语言的教材即可,注意不是当教科书看,而是看完一节过后,打开电脑把后面的习题都写出来,并且编译运行一遍,一定要动手…...

pytorch学习——线性神经网络——1线性回归
概要:线性神经网络是一种最简单的神经网络模型,它由若干个线性变换和非线性变换组成。线性变换通常表示为矩阵乘法,非线性变换通常是一个逐元素的非线性函数。线性神经网络通常用于解决回归和分类问题。 一.线性回归 线性回归是一种常见的机…...

00 - RAP 开发环境配置
文章目录 [1] Eclipse - ADT[2] BTP / S4HC[3] Add ABAP Env. Service[4] Conn. to BTP [1] Eclipse - ADT 关于如何安装配置,参见文章: Install ABAP Development Tools (ADT) and abapGit Plugin Eclipse Eclipse - ADT Eclipse - abapGit Plugin [2] BTP / S4…...

山西电力市场日前价格预测【2023-08-01】
日前价格预测 预测明日(2023-08-01)山西电力市场全天平均日前电价为310.15元/MWh。其中,最高日前电价为335.18元/MWh,预计出现在19: 45。最低日前电价为288.85元/MWh,预计出现在14: 00。 价差方向预测 1:实…...

QT--day5(网络聊天室、学生信息管理系统)
服务器: #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);//给服务器指针实例化空间servernew QTcpServer(this); }Widget::~Widget() {delete ui; …...

【用IDEA基于Scala2.12.18开发Spark 3.4.1 项目】
目录 使用IDEA创建Spark项目设置sbt依赖创建Spark 项目结构新建Scala代码 使用IDEA创建Spark项目 打开IDEA后选址新建项目 选址sbt选项 配置JDK debug 解决方案 相关的依赖下载出问题多的话,可以关闭idea,重启再等等即可。 设置sbt依赖 将sbt…...

HEVC 速率控制(码控)介绍
视频编码速率控制 速率控制: 通过选择一系列编码参数,使得视频编码后的比特率满足所有需要的速率限制,并且使得编码失真尽量小。速率控制属于率失真优化的范畴,速率控制算法的重点是确定与速率相关的量化参数(Quantiz…...

四大软件测试策略的特点和区别(单元测试、集成测试、确认测试和系统测试)
四大软件测试策略分别是单元测试、集成测试、确认测试和系统测试。 一、单元测试 单元测试也称为模块测试,它针对软件中的最小单元(如函数、方法、类、模块等)进行测试,以验证其是否符合预期的行为和结果。单元测试通常由开发人…...

ingress-nginx controller安装
文章目录 一、ingress-nginx controller安装环境 1.1 部署yaml1.2 镜像1.3 安装操作 一、ingress-nginx controller安装 环境 kubernetes版本:1.27.1操作系统:CentOS7.9 1.1 部署yaml deploy.yaml apiVersion: v1 kind: Namespace metadata:labels:…...

开源快速开发平台:做好数据管理,实现流程化办公!
做好数据管理,可以提升企业的办公协作效率,实现数字化转型。开源快速开发平台是深受企业喜爱的低代码开发平台,拥有多项典型功能,是可以打造自主可控快速开发平台,实现一对一框架定制的软件平台。在快节奏的社会中&…...

基于深度学习的裂纹图像分类研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...