现代编程语言都具备的Lambda到底是个啥?详聊Lambda与函数式接口

1. Lambda

咱们首先来说说 Lambda 这个名字,Lambda 并不是一个什么的缩写,它是希腊第十一个字母 λ 的读音,同时它也是微积分函数中的一个概念,所表达的意思是一个函数入参和出参定义,在编程语言中其实是借用了数学中的 λ,并且多了一点含义,在编程语言中功能代表它具体功能的叫法是匿名函数(Anonymous Function),根据百科的解释:

匿名函数(英语:Anonymous Function)在计算机编程中是指一类无需定义标识符(函数名)的函数或子程序。

到这我们应该看懂了,在编程语言中引入了 λ 的数学中的意思后,还加入了“匿名”这个概念,为什么要加它呢?显然是为了让开发者写起来更加方便,不必去想具体的函数名,尤其是在流式表达中,匿名能让你更加高效。

接着再来说说Lambda 的历史,虽然它在 JDK8 发布之后才正式出现,但是在编程语言界,它是一个具有悠久历史的东西,最早在 1958 年在Lisp 语言中首先采用,而且虽然Java脱胎于C++,但是C++在2011年已经发布了Lambda 了,但是 JDK8 的 LTS 在2014年才发布,所以 Java 被人叫做老土不是没有原因的,现代编程语言则是全部一出生就自带 Lambda 支持,所以Lambda 其实是越来越火的一个节奏~

那么Lambda 到底好在哪?不用写函数名?其实我觉得要回答这个问题首先要明白Lambda 在编程语言方面到底是什么?

上面也说了,Lambda 在编程语言中往往是一个匿名函数,也就是说Lambda 是一个抽象概念,而编程语言提供了配套支持,比如在 Java 中其实为Lambda 进行配套的就是函数式接口,通过函数式接口生成匿名类和方法进行Lambda 式的处理。

那么,既然是这一套规则我们明白了,那么Lambda 所提供的好处在Java中就是函数式接口所提供的能力了,函数式接口往往则是提供了一些通用能力,这些函数式接口在JDK中也有一套完整的实践,那就是 Stream。

Stream 提供了一套完整的流式处理方法帮助我们进行流式调用,熟悉Stream 的读者应该知道使用它能带来多么大的便捷,更多关于 Stream 的知识可以看我的 延迟执行与不可变,系统讲解JavaStream数据处理 ,在这篇文章中有着详细的叙述。

那么总结起来,Lambda 在Java中所提供的好处就是使用函数式接口对一些问题进行了抽象,从而得到了一些通用能力,这些通用能力就是使用Lambda 最大的好处,下面将会具体讲解JDK中都定义了哪些通用能力,看到这的小伙伴可以给本文点个赞,以示鼓励。

2. 函数式接口

在 Java 中,所有的函数式接口都是以 @Functionallnterface 进行标注的,就像这样:

@FunctionalInterfacepublic interface Runnable { public abstract void run();}复制代码

在一个接口上打上 @Functionallnterface并且定义一个抽象方法,这样的类我们就称之为函数式接口,当然这个方法并不一定非要用抽象关键字来修饰,比如:

@FunctionalInterfacepublic interface Consumer { void accept(T t);}复制代码

当然,不写 @Functionallnterface 注解其实也没关系,但是需要保证,这个接口只定义了一个抽象方法,接口的默认方法不算,那个可以称得上是接口的静态方法了。

为什么只能有一个抽象方法呢?因为你的自定义逻辑就是这个方法的匿名函数,最终会调用这个方法,所以只能有一个。

然后你就可以使用 Lambda 表达式来进行书写了,就像这样:

Thread thread = new Thread(() -> { });复制代码

看吧,很方便的写法就定义了一个Runnable 的匿名子类出来,不过 Runnable 这种使用Lambda 只是为了生成一个匿名子类的情况确实无法完全发挥Lambda 的作用,Lambda 更大的作用还是在解决具体的问题上,而非创造一个匿名类。

举个例子,假如你想定义一个对商品数据进行商品筛查的函数,那么它可能是这样的:

public List filter(List list, String type) { List result = new ArrayList(); for (Goods goods : list) { if (goods.getType().equals(type)) { result.add(goods); } } return result; }复制代码

ok,看起来一切没问题,但是架不住需求改变啊,很快你又需要定义一个对商品金额进行筛查的方法,那么它可能是这样的:

public List gt(List list, Integer price) { List result = new ArrayList(); for (Goods goods : list) { if (goods.getPrice() > price) { result.add(goods); } } return result; }复制代码

那么你可以发现:大部分代码基本没变,只有入参和判断逻辑发生了一点改变,这个时候你可能会想,能不能把判断逻辑直接抽象成一个匿名函数,每次只需要简单写一个这个判断函数即可,再把方法入参封装成一个东西,在任何场景下都可以使用。

看到这,你可能就有点明白了,因为在Java8 已经提供了Stream流去做这件事,上面这个场景其实对应的是Stream 中的filter 方法,而filter 方法的入参就是一个函数式接口——Predicate。

还没明白吗?那我说的再清楚一点,Predicate 抽象了判断这个场景,而且这种抽象不局限于业务,是直接对某一类场景进行抽象,比如筛选商品类别,筛选商品大于某个金额或者小于某个金额,它不在纠结你到底想要怎么筛选,而是直接对筛选函数进行抽象,得到了Predicate,你想怎么筛选你自己写,剩下的交给它,它将一劳永逸的解决这类问题,当然这里面还有一部分 Stream 的功劳,不过主要思想还是 Predicate 在做,Stream 这里我们暂且不提。

像这种对于某个场景进行顶级抽象的函数式接口,JDK一共提供了四个:

  • Consumer
  • Supplier
  • Predicate
  • Function
  • 接下来我将一一为大家进行讲述,除了这四个之外还有大量的衍生函数式接口,在JDK8中就有50个左右,不过都是在这四个基础上进行修改,不必担心记不住的问题。

    3. Consumer

    **Consumer **通过名字可以看出它是一个消费函数式接口,主要针对的是消费这个场景,它的代码定义如下:

    @FunctionalInterfacepublic interface Consumer { void accept(T t);}复制代码

    通过泛型 T 定义了一个入参,但是没有返回值,它代表你可以针对这个入参做一些自定义逻辑,比较典型的例子是 Stream 中的 forEach 方法。

    而我们的主要使用场景也往往是循环进行某项操作,比如有一堆手机号,循环进行发短信。

    所以消费场景是 Consumer 的主要用武之地,但是有时候你还面临一个问题,一个入参似乎太少了,有时候你需要对两个对象进行操作,又懒得将它们合并成一个对象,这种情况 JDK 提供了 BiConsumer:

    @FunctionalInterfacepublic interface BiConsumer { void accept(T t, U u);}复制代码

    这种你可以直接传进去两个参数了,什么?你想要三个参数的?那没有,三个或者三个以上我感觉就有必要合并成一个对象进行消费了。

    除了这两个之外,还有DoubleConsumer、IntConsumer和LongConsumer这种限定了入参类型的 Consumer,这里不再多述。

    4. Supplier

    Supplier通过名字比较难看出来它是一个场景的函数式接口,它主要针对的是get这个场景或者说获取这个场景,它的代码定义如下:

    @FunctionalInterfacepublic interface Supplier { T get();}复制代码

    通过泛型 T 定义了一个返回值类型,但是没有入参,它代表你可以针对调用方获取某个值,比较典型的例子是 Stream 中的 collect 方法,通过自定义传入我们想要取得的某种对象进行对象收集。

    而我们的主要使用场景也往往是收集和聚合这个场景了,这个场景我们也是对获得这个场景进行收集。

    和Consumer一样,Supplier还具有以下衍生接口:

  • BooleanSupplier
  • DoubleSupplier
  • IntSupplier
  • LongSupplier
  • 都是提前对获取的定义好了数据类型,思想一致,这里不再多述。

    5. Predicate

    Predicate前文我们已经介绍过,它主要针对的是判断这个场景,它的代码定义如下:

    @FunctionalInterfacepublic interface Predicate { boolean test(T t);}复制代码

    通过泛型 T 定义了一个入参,返回了一个布尔值,它代表你可以传入一段判断逻辑的函数,比较典型的例子是 Stream 中的 filter方法。

    我们对于它的使用场景实在是太多了,基本上做任何业务都有在内存中进行筛选 or 判断的场景。

    所以判断和筛选场景是 Predicate的主要用武之地,但是有时候你还面临和上面一样的问题,一个入参似乎太少了,有时候你需要对两个对象进行操作,又懒得将它们合并成一个对象,这种情况 JDK 提供了 BiPredicate:

    @FunctionalInterfacepublic interface BiPredicate { boolean test(T t, U u);}复制代码

    这种你可以直接传进去两个参数进行函数的自定义逻辑。

    除了这两个之外,还有DoublePredicate、IntPredicate和LongPredicate这种限定了入参类型的Predicate,这里不再多述。

    6. Function

    Function 接口的名字不太能轻易看出来它的场景,它主要针对的则是 转换这个场景,其实说转换可能也不太正确,它是一个覆盖范围比较广的场景,你也可以理解为扩展版的Consumer,接口定义如下:

    @FunctionalInterfacepublic interface Function { R apply(T t);}复制代码

    通过一个入参 T 进行自定义逻辑处理,最终得到一个出参 R,比较典型的例子是 Stream 中的 map 系列方法和 reduce 系列方法。

    为什么我说也可以理解为一个扩展版的Consumer呢?我们还举例手机号发短信的场景好了,你通过循环发完短信之后可能想拿到发完短信之后的结果对象,来进行后续处理。

    这个时候单纯的Consumer就不行了,因为它没有返回值,你就可以通过 Function 这种函数式对象进行处理了。

    和 Consumer 一样,Function 也有一个衍生接口可以通过两个入参返回一个对象——BiFunction。

    还有一些定义好了入参和出参的 Function

  • 延迟执行与不可变,系统讲解JavaStream数据处理
  • 归约、分组与分区,深入讲解JavaStream终结操作
  • 郑重声明:本文内容及图片均整理自互联网,不代表本站立场,版权归原作者所有,如有侵权请联系管理员(admin#wlmqw.com)删除。
    (0)
    用户投稿
    上一篇 2022年7月24日
    下一篇 2022年7月24日

    相关推荐

    • APK安装流程:点击下载应用了解安装的全过程

      一讲到APK安装流程,它有四种安装方式: 系统应用和预制应用安装,开机时完成,没有安装界面,在PKMS的构造函数中欧冠完成安装 网络下载应用安装,通过应用商店来完成,调用Packa…

      2022年7月9日
    • 小孩咳嗽老不好,有浓痰,止咳化痰哪种方法好?

      5岁孩子咳嗽有痰,脓痰什么原因1、咳嗽有痰往往为湿性咳嗽,脓痰,大多数与细菌感染有关,比如肺炎链球菌,卡他莫拉菌,金黄色葡萄球菌等等。 2、咳嗽有痰,可能来自急性上呼吸道,比如细菌…

      2022年8月22日
    • 西班牙公主姐妹花闪耀亮相!莱昂诺尔又输了,妹妹穿迷你裙更惊艳

      西班牙王室的公主姐妹花最近亮相,个子又长高了不少,两人的身高都差不多有175公分,超过了有超模身材的莱蒂齐亚王后。当地时间周六,西班牙王室一家四口亮相,莱后和国王带着两个女儿去剧院…

      2022年6月27日
    • 2台“Studio Display”的桌面,是一种什么体验?

      在等了三个月之后,苹果终于把Studio Display发过来了,我都快忘了还买了台显示器… 我终于可以把两台“Studio Display”一起摆在桌面上了,今天主要…

      2022年6月18日
    • 一个控制买衣服的方法,很有效

      这个方法叫“七套法则”,就是以一个月为单位,找出你认为最好看的七套衣服,这七套衣服穿一周,然后轮流穿一个月。 以八月份为例,首先在所有的衣服中,找到你觉得最好的七套,如西服套装、碎…

      2022年8月12日
    • 美国第二季度GDP同比下降0.9% 连续两个季度出现负值

      中新网7月28日电 据美联社28日报道,美国商务部数据显示,美国第二季度实际GDP年化季率初值显示,美国第二季度GDP同比下降0.9%。 这是美国GDP连续第二个季度出现下降。此前…

      2022年8月4日
    • 中年女人“扮嫩”有段位!学会这“2点建议,3个方法”,就是高手

      到底应该如何“扮嫩”?才能神不知鬼不觉,对于40、50岁的年龄阶段,到底是成熟更加重要?还是减龄更有效?无论是哪一种,都要记住自己选择的路,就算拥有再难的挫折和困难,也要坚持走下去…

      2022年7月24日
    • 399元能买到B660M?昂达这块主板真高性价比

      399元、B660、MATX、双M.2接口、DDR4内存,以及纯白PCB,这几个关键字其实还真难凑到到一起。但事实上就在前段时间,昂达推出了一款刷新了行业新低价399元的B660S…

      2022年8月2日
    • 一个包两个包?90分巨能装双肩包以一敌二

      短期出差、旅游时你是否还在辛辛苦苦带着好几个包?我还记得每次短期外出时都是一手提着电脑包,一手提着几日的换洗衣服,背上又要背着办公材料和日用品。有朋友建议我直接都装行李箱里携带,但…

      2022年8月13日
    • 智己汽车从多维角度出发,探讨“智能汽车该有的样子”

      汽车作为一项重要的出行交通工具,人们对它的各项性能都非常关注。随着智能的话题日益高涨,对于智能汽车人们也提出了新的要求。而智己汽车正是抓住了用户的这一心理,推出的“新世界驾控旗舰”…

      2022年6月18日

    联系我们

    联系邮箱:admin#wlmqw.com
    工作时间:周一至周五,10:30-18:30,节假日休息