当前位置: 首页 > news >正文

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并返回StringLambda。例如,下面就是从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中已经有了几个函数式接口,比如ComparableRunnableCallable

Java 8的库设计师在java.util.function包中引入了几个新的函数式接口:PredicateConsumerFunction

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说明,可能会注意到诸如andor等其他方法。现在不用太计较这些。

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类型要么是引用类型(比如ByteIntegerObjectList),要么是原始类型(比如intdoublebytechar)。但是泛型(比如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);

一般来说,针对专门的输入参数类型的函数式接口的名称都要加上对应的原始类型前缀,比如DoublePredicateIntConsumerLongBinaryoperatorIntFunction等。Function接口还有针对输出参数类型的变种:ToIntFunction<T>IntToDoubleFunction等。

下表总结了Java API中提供的最常用的函数式接口及其函数描述符。请记得这只是一个起点。如果有需要,可以自己设计一个。请记住,(T, U) -> R的表达方式展示了应当如何思考一个函数描述符。表的左侧代表了参数类型。这里它代表一个函数,具有两个参数,分别为泛型TU,返回类型为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付诸实践&#xff1a;环绕执行模式第1步&#xff1a;记得行为参数化第2步&#xff1a;使用函数式接口来传递行为第3步&#xff1a;执行一个行为第4步&#xff1a;传递Lambda 使用函数式接口PredicateConsumerFunction原始类型特化 Lambda表…...

大数据开发面试必问:Hive调优技巧系列一

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

Jupyter Notebook 7重磅发布,新增多个特性!

本文分享Jupyter Notebook大版本v7.0.0更新亮点&#xff0c;及简单测试&#xff01; 近日&#xff0c;Jupyter Notebook大版本v7.0.0更新&#xff0c;Jupyter Notebook 7基于JupyterLab&#xff0c;因此它包含了过去几年JupyterLab中添加的许多新功能和改进&#xff0c;部分亮…...

linux V4L2子系统——v4l2架构(1)之整体架构

概述 V4L&#xff08;Video for Linux&#xff09;是Linux内核中关于视频设备的API接口&#xff0c;涉及视频设备的音频和视频信息采集及处理、视频设备的控制。V4L出现于Linux内核2.1版本&#xff0c;经过修改bug和添加功能&#xff0c;Linux内核2.5版本推出了V4L2&#xff08…...

Qt信号与槽机制的本质

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

Linux:入门学习知识及常见指令

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

K8s:Kubernetes 故障排除方法论

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

TCP 三次握手四次挥手浅析

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

【软件安装】MATLAB_R2021b for mac 安装

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

电脑维护:10妙招,让你的电脑更加稳定!

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

大数据面试题:Kafka的单播和多播

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

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上的操作系统&#xff08;一&#xff09;—— 环境搭建_riscv开发环境_Patarw_Li的博客-CSDN博客 开发一个RISC-V上的操作系统&#xff08;二&#xff09;—— 系统引导程序&a…...

Mybatis-plus集合

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

C++ 结构体和联合体

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

使用TensorFlow训练深度学习模型实战(下)

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

lucene、solr、es的区别以及应用场景

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

Java方法的使用(重点:形参和实参的关系、方法重载、递归)

目录 一、Java方法 * 有返回类型&#xff0c;在方法体里就一定要返回相应类型的数据。没有返回类型&#xff08;void&#xff09;&#xff0c;就不要返回&#xff01;&#xff01; * 方法没有声明一说。与C语言不同&#xff08;C语言是自顶向下读取代码&#xff09;&#…...

登录页的具体实现 (小兔鲜儿)【Vue3】

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

大学如何自学嵌入式开发?

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

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...