Java8实战-总结9

news2024/11/15 12:05:00

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吧。

	@FunctionalInterface
	public 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"))) {
		//处理BufferedReader
		return 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表达式,如下所示:

	@FunctionalInterface
	public 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);
	}
};

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

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

相关文章

mybatis plus 的一些使用

简介 官网&#xff1a;http://mp.baomidou.com/ 参考教程&#xff1a;https://baomidou.com/pages/24112f/ MyBatis-Plus&#xff08;简称 MP&#xff09;是一个 MyBatis 的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。 …

web前端框架Javascript之JavaScript 异步编程史

早期的 Web 应用中&#xff0c;与后台进行交互时&#xff0c;需要进行 form 表单的提交&#xff0c;然后在页面刷新后给用户反馈结果。在页面刷新过程中&#xff0c;后台会重新返回一段 HTML 代码&#xff0c;这段 HTML 中的大部分内容与之前页面基本相同&#xff0c;这势必造成…

同样是跨端框架,React会不会被VUE取代?

看到知乎上有比较多的类似问题&#xff0c;正好这两个框架在以往的一些项目中都有实践过&#xff0c;就借着本篇文章说说我个人的看法。 先摆个结论&#xff1a;不会&#xff0c;毕竟各有千秋&#xff0c;除非跨端框架有被更好的概念所替代&#xff0c;又或者App已经彻底过气了…

PoseiSwap:通过 RWA 的全新叙事,反哺 Nautilus Chain 生态

PoseiSwap 是 Nautilus Chain 上的首个 DEX&#xff0c;作为目前行业内模块化区块链叙事的早期奉行者&#xff0c;PoseiSwap 也得到了较高的市场关注。基于 Nautilus Chain&#xff0c;PoseiSwap 打造了一个全新的 Rollup 应用层&#xff0c;并通过零知识证明来建立全新的订单簿…

6个月、21天,GoldenDB分布式数据库核心系统落地中移动

近日&#xff0c;2023“鼎新杯”数字化转型应用大赛入围名单公示&#xff0c;山东移动基于GoldenDB分布式数据库的CRM&BOSS核心系统自主创新实践成功入选。该项目是中兴通讯与中国移动在数据库关键领域的又一个合作范例。 核心系统业务量大&#xff0c;分布式转型迫在眉睫 …

C语言指针进阶-1

本篇文章带来 1. 字符指针 2. 数组指针 3. 指针数组的相关知识详细讲解&#xff01; 如果您觉得文章不错&#xff0c;期待你的一键三连哦&#xff0c;你的鼓励是我创作的动力之源&#xff0c;让我们一起加油&#xff0c;一起奔跑&#xff0c;让我们顶峰相见&#xff01;&#…

Qt信号与槽机制的本质

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

229. 多数元素 II

229. 多数元素 II 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a; 原题链接&#xff1a; 229. 多数元素 II https://leetcode.cn/problems/majority-element-ii/description/ 完成情况&#xff1a; 解题思路&#xff1a; 我们用哈希统…

想学嵌入式开发,薪资怎么样?

对于嵌入式工程师来说呢&#xff0c;它重点学习内容就是首先一定要打好基础&#xff0c;如果从编程语言角度来讲&#xff0c;那么可以在语言上选C或者C&#xff0c;你可以选择其中任何一门语言作为你的入门。当然从入门角度来讲&#xff0c;其实C语言要比C要容易一些&#xff0…

flag{网鼎杯之java代码审计入门} - file-in-java[ctf]

一、赛题截图 二、接口测试 我们先上传文件抓包&#xff0c;发送到repeter 响应如下 我们使用下载接口去下载一个不存在的文件&#xff0c;回显“资源被删除” - 说明系统可能去查找了这个文件&#xff0c;那我们能不能去下载/etc/passwd文件&#xff0c;但是还不知道相对…

HTML5中的data-*属性

介绍&#xff1a; data-*全局属性是一类被称为自定义数据属性的属性&#xff0c;它赋予我们在所有 HTML 元素上嵌入自定义数据属性的能力。 data-*的使用 <div class"child" data-name"小红" data-age"18"></div> 在js里有两种获…

无涯教程-jQuery - height( val )方法函数

height(val)方法设置每个匹配元素的CSS高度。 height( val ) - 语法 selector.height( val ) 这是此方法使用的所有参数的描述- val - 这是元素的高度。如果未指定任何显式单位(如em或&#xff05;)&#xff0c;则将" px"连接到该值。 height( val ) - 示例…

Xshell使用是出现全黑或全白问题

Xshell使用是出现全黑或全白问题&#xff0c;这是我实际遇到的问题如图。 解决方式&#xff1a; 设置字体 解决成功&#xff1a;

uniapp uni-combox 下拉提示无匹配项(完美解决--附加源码解决方案及思路)

问题描述 匆匆忙忙又到了周一啦&#xff0c;一大早就来了一个头疼的问题&#xff0c;把我难得团团转&#xff0c;呜呜呜~ 下面我用代码的方式展示出来&#xff0c;看下你的代码是否与我的不同。 解决方案 <uni-forms-item label"名称" name"drugName&quo…

Mybatis-plus集合

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

八股文的天花板,没到 35k 的 Java 开发都值得好好读一读

确实&#xff0c;在当下行情之下&#xff0c;还能保持“有恃无恐”的人&#xff0c;那他自身肯定得有两把刷子。 谁不想当一个“技术大牛”&#xff1f; 谁不想年纪轻轻就“年薪百万”&#xff1f; 但“梦想美好&#xff0c;现实残酷”&#xff0c;不少人实际工作中做的事&a…

C#中 使用yield return 优化大数组或集合的访问

概要 我们在开发过程中&#xff0c;经常需要在一个很大的数组或集合中搜索元素&#xff0c;以满足业务需求。 本文主要介绍通过使用yield return的方式&#xff0c;避免将大量数据全部加载进入内存&#xff0c;再进行处理。从而提高程序的性能。 设计和实现 基本业务场景&a…

如何压缩MP4视频?学会这样压缩很简单

怎么压缩MP4视频大小呢&#xff1f;如果需要将视频存储在手机或平板电脑等设备上&#xff0c;通常也需要将视频大小压缩到适当的大小&#xff0c;以节省存储空间。此时&#xff0c;可以根据设备的存储容量和需要存储的其他文件来选择视频压缩的大小。很多小伙伴不知道怎么压缩视…

24考研数据结构-第三章:栈和队列

目录 第三章 栈和队列3.1栈&#xff08;stack&#xff09;3.1.1栈的基本概念栈的基本概念知识回顾 3.1.2 栈的顺序存储上溢与下溢栈的顺序存储知识回顾 3.1.3栈的链式存储链栈的基本操作 3.2队列&#xff08;Queue&#xff09;3.2.1队列的基本概念3.2.2队列的顺序存储结构3.2.2…

Python基础教程:Socket网络编程

网络编程是指编写程序使其能够通过网络连接与其他计算机进行通信。Python 作为一种强大的脚本语言&#xff0c;也提供了丰富的库来支持网络编程&#xff0c;如 socket、asyncio、http.client 等。在这篇教程中&#xff0c;我们将介绍如何使用 socket 库实现简单的网络编程。 1…