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

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

Python使用国内镜像加速pip安装的方法讲解

《Python使用国内镜像加速pip安装的方法讲解》在Python开发中,pip是一个非常重要的工具,用于安装和管理Python的第三方库,然而,在国内使用pip安装依赖时,往往会因为网络问题而导致速... 目录一、pip 工具简介1. 什么是 pip?2. 什么是 -i 参数?二、国内镜像源的选择三、如何

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程