java lambda有必要_Java的Lambda表达式有何用处?如何使用?

2024-01-15 15:50

本文主要是介绍java lambda有必要_Java的Lambda表达式有何用处?如何使用?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文转载自知乎,之前刚接触到Lambda表达式,看了好多文章,看完也还是一脸懵逼,后来刷知乎刷到这篇文章,顿开茅塞,让我明白了Lambda表达式到底是个啥,咋用。最重要的是第一点,知道了这个,其他的要搞清楚就很轻松了,所以推荐给大家。

ps:本文已经获得作者Mingqi的转载授权,如需转载请私信原作者。

5b80a69e1e91c0d24ddc63569b0592d9.png

什么是Lambda?

我们知道,对于一个Java变量,我们可以赋给其一个“值”。

c1fb0f4c655fb8358fad8c35b27f5fb5.png

如果你想把“一块代码”赋给一个Java变量,应该怎么做呢?

比如,我想把右边那块代码,赋给一个叫做aBlockOfCode的Java变量:

55246ad144abdfb8c042f208099391da.png

在Java 8之前,这个是做不到的。但是Java 8问世之后,利用Lambda特性,就可以做到了。

2e9b91926ab0f7b62851877a10b9c7fa.png

当然,这个并不是一个很简洁的写法。所以,为了使这个赋值操作更加elegant, 我们可以移除一些没用的声明。

7f83705a907ce25b20dc916e4a5bd684.png

这样,我们就成功的非常优雅的把“一块代码”赋给了一个变量。而“这块代码”,或者说“这个被赋给一个变量的函数”,就是一个Lambda表达式。

但是这里仍然有一个问题,就是变量aBlockOfCode的类型应该是什么?

在Java 8里面,所有的Lambda的类型都是一个接口,而Lambda表达式本身,也就是”那段代码“,需要是这个接口的实现。这是我认为理解Lambda的一个关键所在,简而言之就是,Lambda表达式本身就是一个接口的实现。直接这样说可能还是有点让人困扰,我们继续看看例子。我们给上面的aBlockOfCode加上一个类型:

3d9150da0a47d14a7e8bf9df403ea580.png

这种只有一个接口函数需要被实现的接口类型,我们叫它”函数式接口“。为了避免后来的人在这个接口中增加接口函数导致其有多个接口函数需要被实现,变成"非函数接口”,我们可以在这个上面加上一个声明@FunctionalInterface, 这样别人就无法在里面添加新的接口函数了:

a524c2a46af23cba8b97b076154e6202.png

这样,我们就得到了一个完整的Lambda表达式声明:

443cf20853bfe47996bda2b938c92f5b.png

06d9daade29cc94f585edf22a1eebc6f.png

Lambda表达式有什么作用?

最直观的作用就是使得代码变得异常简洁。

我们可以对比一下Lambda表达式和传统的Java对同一个接口的实现:

eacd8271b2a0eb672f859b52245d47e1.png

这两种写法本质上是等价的。但是显然,Java 8中的写法更加优雅简洁。并且,由于Lambda可以直接赋值给一个变量,我们就可以直接把Lambda作为参数传给函数, 而传统的Java必须有明确的接口实现的定义,初始化才行:

35d4cd32bb30ad2e209d559bc99e09c5.png

有些情况下,这个接口实现只需要用到一次。传统的Java 7必须要求你定义一个“污染环境”的接口实现MyInterfaceImpl,而相较之下Java 8的Lambda, 就显得干净很多。

d9d28208c4d8dc4b5ffa70fed47f2516.png

Lambda结合FunctionalInterface Lib, forEach, stream(),method reference等新特性可以使代码变的更加简洁!

直接上例子。

假设Person的定义和List的值都给定。

d8d855e394984213bd5ed53bded2372d.png

现在需要你打印出guiltyPersons List里面所有LastName以"Z"开头的人的FirstName。

原生态Lambda写法:定义两个函数式接口,定义一个静态函数,调用静态函数并给参数赋值Lambda表达式。

e00c99110b3b054ba7e2305fc5f87615.png

这个代码实际上已经比较简洁了,但是我们还可以更简洁么?

当然可以。在Java 8中有一个函数式接口的包,里面定义了大量可能用到的函数式接口(java.util.function (Java Platform SE 8 ))。所以,我们在这里压根都不需要定义NameChecker和Executor这两个函数式接口,直接用Java 8函数式接口包里的Predicate和Consumer就可以了——因为他们这一对的接口定义和NameChecker/Executor其实是一样的。

36d3fbc6a22eff7a16e0996e2a530917.png

第一步简化 - 利用函数式接口包:

ff2b2a282de34166afcf10a4ae1c5e44.png

静态函数里面的for each循环其实是非常碍眼的。这里可以利用Iterable自带的forEach()来替代。forEach()本身可以接受一个Consumer 参数。

第二步简化 - 用Iterable.forEach()取代foreach loop:

233a007be66c11f00a5613ab3a3ef422.png

由于静态函数其实只是对List进行了一通操作,这里我们可以甩掉静态函数,直接使用stream()特性来完成。stream()的几个方法都是接受Predicate,Consumer等参数的(java.util.stream (Java Platform SE 8 ))。你理解了上面的内容,stream()这里就非常好理解了,并不需要多做解释。

第三步简化 - 利用stream()替代静态函数:

17ea3389dbc72a919c0d145bddf2d00c.png

对比最开始的Lambda写法,这里已经非常非常简洁了。但是如果,我们的要求变一下,变成print这个人的全部信息,及p -> System.out.println(p); 那么还可以利用Method reference来继续简化。所谓Method reference, 就是用已经写好的别的Object/Class的method来代替Lambda expression。格式如下:

e54c3248ab1d71430b21334d83ad6fae.png

第四步简化 - 如果是println(p),则可以利用Method reference代替forEach中的Lambda表达式:

70ef05dc89c3b26b238c37d4fa270da0.png

这基本上就是能写的最简洁的版本了。

d190a5c74966a65860e071a44056c8fe.png

Lambda配合Optional可以使Java对于null的处理变的异常优雅

这里假设我们有一个person object,以及一个person object的Optional wrapper:

fd7e6aa21bb2db83c043eca8bb5a019d.png

Optional如果不结合Lambda使用的话,并不能使原来繁琐的null check变的简单。

3d33516e1f646b943542048f714a6755.png

只有当Optional结合Lambda一起使用的时候,才能发挥出其真正的威力!

我们现在就来对比一下下面四种常见的null处理中,Java 8的Lambda+Optional和传统Java两者之间对于null的处理差异。

情况一 - 存在则开干

714a4827110ebee23d7f44284d61bbc4.png

情况二 - 存在则返回,无则返回屁

2c22376efcc1c7b72be68732908d9b10.png

情况三 - 存在则返回,无则由函数产生

2832b72960287747dbb1acf541f644fa.png

情况四 - 夺命连环null检查

524a033bc32530c4fcac80695616d53a.png

由上述四种情况可以清楚地看到,Optional+Lambda可以让我们少写很多ifElse块。尤其是对于情况四那种夺命连环null检查,传统java的写法显得冗长难懂,而新的Optional+Lambda则清新脱俗,清楚简洁。

e920e8f6b5dc839677e7730bee382ded.png

关于Java的Lambda, 还有东西需要讨论和学习。比如如何handle lambda exception,如何利用Lambda的特性来进行parallel processing等。总之,我只是一如既往地介绍个大概,让你大概知道,哦!原来是这样子就OK了。网上关于Lambda有很多相关的教程,多看多练。假以时日,必定有所精益。

作者:Mingqi

链接:https://www.zhihu.com/questio...

来源:知乎

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

这篇关于java lambda有必要_Java的Lambda表达式有何用处?如何使用?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/609416

相关文章

SpringBoot中六种批量更新Mysql的方式效率对比分析

《SpringBoot中六种批量更新Mysql的方式效率对比分析》文章比较了MySQL大数据量批量更新的多种方法,指出REPLACEINTO和ONDUPLICATEKEY效率最高但存在数据风险,MyB... 目录效率比较测试结构数据库初始化测试数据批量修改方案第一种 for第二种 case when第三种

Java docx4j高效处理Word文档的实战指南

《Javadocx4j高效处理Word文档的实战指南》对于需要在Java应用程序中生成、修改或处理Word文档的开发者来说,docx4j是一个强大而专业的选择,下面我们就来看看docx4j的具体使用... 目录引言一、环境准备与基础配置1.1 Maven依赖配置1.2 初始化测试类二、增强版文档操作示例2.

一文详解如何使用Java获取PDF页面信息

《一文详解如何使用Java获取PDF页面信息》了解PDF页面属性是我们在处理文档、内容提取、打印设置或页面重组等任务时不可或缺的一环,下面我们就来看看如何使用Java语言获取这些信息吧... 目录引言一、安装和引入PDF处理库引入依赖二、获取 PDF 页数三、获取页面尺寸(宽高)四、获取页面旋转角度五、判断

Spring Boot中的路径变量示例详解

《SpringBoot中的路径变量示例详解》SpringBoot中PathVariable通过@PathVariable注解实现URL参数与方法参数绑定,支持多参数接收、类型转换、可选参数、默认值及... 目录一. 基本用法与参数映射1.路径定义2.参数绑定&nhttp://www.chinasem.cnbs

C++中assign函数的使用

《C++中assign函数的使用》在C++标准模板库中,std::list等容器都提供了assign成员函数,它比操作符更灵活,支持多种初始化方式,下面就来介绍一下assign的用法,具有一定的参考价... 目录​1.assign的基本功能​​语法​2. 具体用法示例​​​(1) 填充n个相同值​​(2)

JAVA中安装多个JDK的方法

《JAVA中安装多个JDK的方法》文章介绍了在Windows系统上安装多个JDK版本的方法,包括下载、安装路径修改、环境变量配置(JAVA_HOME和Path),并说明如何通过调整JAVA_HOME在... 首先去oracle官网下载好两个版本不同的jdk(需要登录Oracle账号,没有可以免费注册)下载完

Spring StateMachine实现状态机使用示例详解

《SpringStateMachine实现状态机使用示例详解》本文介绍SpringStateMachine实现状态机的步骤,包括依赖导入、枚举定义、状态转移规则配置、上下文管理及服务调用示例,重点解... 目录什么是状态机使用示例什么是状态机状态机是计算机科学中的​​核心建模工具​​,用于描述对象在其生命

Spring Boot 结合 WxJava 实现文章上传微信公众号草稿箱与群发

《SpringBoot结合WxJava实现文章上传微信公众号草稿箱与群发》本文将详细介绍如何使用SpringBoot框架结合WxJava开发工具包,实现文章上传到微信公众号草稿箱以及群发功能,... 目录一、项目环境准备1.1 开发环境1.2 微信公众号准备二、Spring Boot 项目搭建2.1 创建

Java中Integer128陷阱

《Java中Integer128陷阱》本文主要介绍了Java中Integer与int的区别及装箱拆箱机制,重点指出-128至127范围内的Integer值会复用缓存对象,导致==比较结果为true,下... 目录一、Integer和int的联系1.1 Integer和int的区别1.2 Integer和in

SpringSecurity整合redission序列化问题小结(最新整理)

《SpringSecurity整合redission序列化问题小结(最新整理)》文章详解SpringSecurity整合Redisson时的序列化问题,指出需排除官方Jackson依赖,通过自定义反序... 目录1. 前言2. Redission配置2.1 RedissonProperties2.2 Red