函数式编程及应用

news2024/11/18 15:21:40

目录

    • 什么是Lambda
    • lambda表达式的类型及实现方式
      • 类型
      • 语法
    • 常用函数式接口
      • Customer
    • 函数式编程在Stream中的应用
    • 总结
    • 参考资料

什么是Lambda

    Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的Java代码。

    Lambda 表达式描述了一个代码块(或者叫匿名方法),可以将其作为参数传递给构造方法或者普通方法以便后续执行。如:

() -> System.out.println("hello");

() 为 Lambda 表达式的参数列表(允许没有参数),-> 标识这串代码为 Lambda 表达式(也就是说,看到 -> 就知道这是 Lambda),System.out.println("hello") 就是执行的代码,将“hello”打印到标准输出流。

    以Runnable接口为例,原来我们创建一个线程并启动它是这样的:

public class Test {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        }).start();
    }
}

如果用 Lambda 表达式只需要这样写:

public class Test {
    public static void main(String[] args) {
        new Thread(() -> System.out.println("hello")).start();
    }
}

两者对比就可以看出代码的优雅程度。

我们看Runnable接口源码:

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

其中@FunctionalInterface注解是个标记注解,说明通过 @FunctionalInterface 标记的接口可以通过 Lambda 表达式创建实例。@FunctionalInterface修饰函数式接口的,要求接口中的抽象方法只有一个,有多个抽象方法编译将报错。但由于该注解是个标记注解,加不加@FunctionalInterface对于接口是不是函数式接口没有影响,即不加该注解的接口也可以作为函数式编程使用。

lambda表达式的类型及实现方式

类型

public interface Comparator<T> {
    int compare(T o1, T o2);
}
public interface Runnable {
    void run();
}
public interface Callable<V> {
    V call() throws Exception;
}

    上面三个接口都只有一个抽象方法,但是三个方法的签名都不一样,这要求Lambda表达式与实现接口的方法签名要一致。下面用函数描述符来表示上述三个方法的签名,箭头前面是方法的入参类型,后面是返回类型。

  1. compare:

    (T, T) -> int
    

    两个泛型T类型的入参,返回int类型

    Lambda表达式:(User u1, User u2) -> u1.getAge - u2.getAge

  2. run:

    () -> void
    

    无入参,无返回值

    Lambda表达式:() -> { System.out.println("hello"); }

  3. call:

    () -> V
    

    无入参,返回一个泛型V类型的对象

    Lambda表达式:() -> new User() //不需要用括号环绕返回值为单行方法调用。

语法

Lambda表达式由三部分组成:

  1. 参数列表
  2. 箭头
  3. 主体

两种风格,分别是:

  1. 表达式-风格

    (parameters) -> expression

  2. 块-风格

    (parameters) -> { statements; }

常用函数式接口

java.util.function包中定义了一些常见的函数式接口:

  • Function,接受一个输入参数,返回一个结果。参数与返回值的类型可以不同,我们之前的map方法内的lambda就是表示这个函数式接口的;
  • Consumer,接受一个输入参数并且无返回的操作。比如我们针对数据流的每一个元素进行打印,就可以用基于Consumer的lambda;
  • Supplier,无需输入参数,只返回结果。看接口名就知道是发挥了对象工厂的作用;
  • Predicate,接受一个输入参数,返回一个布尔值结果。比如我们在对数据流中的元素进行筛选的时候,就可以用基于Predicate的Lambda;

Customer

    如果我们想要将公共的部分抽取出来,发现都比较零散,还不如不抽取,但是不抽取代码又存在大量重复的代码不符合我的风格。于是我便可以使用 Consumer 接口。
如下是个例子(参考的https://xie.infoq.cn/article/047263a6c694daa5d65295b25)

B b = this.baseMapper.selectOne(queryWrapper);
if (b != null) {
  String status = b.getStatus();
  if (Objects.equals(Constants.STATUS_ING, status)){
    return "处理中";
  } else if (Objects.equals(Constants.STATUS_SUCCESS, status)){
    return "处理成功";
  }
  //失败的操作
  //请求第三方接口并解析响应结果
  ......
  if (ReturnInfoEnum.SUCCESS.getCode().equals(parse.getCode())) {
        ......
        //更新B表操作
    bb.setStatus(Constants.STATUS_ING);
    mapper.updateById(bb);

    //更新A表的状态
    a.setStatus(Constants.STATUS_ING);
    aMapper.updateById(a);
  }
  
} else {
  //请求第三方接口并解析响应结果
  ......
  if (ReturnInfoEnum.SUCCESS.getCode().equals(parse.getCode())) {
        ......
        //插入B表操作
    bb.setStatus(Constants.STATUS_ING);
    mapper.insert(bb);

    //更新A表的状态
    a.setStatus(Constants.STATUS_ING);
    aMapper.updateById(a);
  }
}

这个方法若使用普通的方法抽象就比较费劲,那么用Customer就可以抽象为如下的样子

B b = this.baseMapper.selectOne(queryWrapper);
if (b != null) {
  String status = b.getStatus();
  if (Objects.equals(Constants.STATUS_ING, status)){
    return "处理中";
  } else if (Objects.equals(Constants.STATUS_SUCCESS, status)){
    return "处理成功";
  }
  //失败的操作
  getResponse(dto, response, s -> mapper.insert(s));
} else {
  getResponse(dto, response, s -> mapper.updateById(s));
}

public void getResponse(DTO dto, Response response, Consumer<B> consumer){
  //请求第三方接口并解析响应结果
  ......
  if (ReturnInfoEnum.SUCCESS.getCode().equals(parse.getCode())) {
        ......
    bb.setStatus(Constants.STATUS_ING);
  
    consumer.accept(bb);

    //更新A表的状态
    a.setStatus(Constants.STATUS_ING);
    aMapper.updateById(a);
  }
}

这样抽象之后,代码就很简洁,而且易读性强。

    Supplier若在业务中使用其实就是在对象工厂中使用,但是感觉多此一举,Supplier真正的用武之地是对一个复杂操作返回结果进行包装。Predicate也是同样。

函数式编程在Stream中的应用

在这里插入图片描述
    以上是Stream的api,可以看到,api的入参大量使用了函数式编程。其中,这些操作可以分为两类:中间操作(Intermediate Operations)和终止操作(Terminal Operations)。 中间操作是对 Stream 进行转换的操作,它们返回一个新的 Stream。以下对一些常见的中间操作进行解释:

filter(Predicate):过滤 Stream 中满足条件的元素
map(Function<T, R>):将 Stream 中的元素转换为另一种类型
flatMap(Function<T, Stream>):将 Stream 中的元素转换为其他 Stream,然后将这些 Stream 合并为一个 Stream
distinct():去除 Stream 中的重复元素
sorted():对 Stream 中的元素进行排序,可以传入自定义的 Comparator终止操作是对 Stream 进行最终处理的操作,它们返回一个结果或产生一个副作用。以下是一些常见的终止操作:
forEach(Consumer):对 Stream 中的每个元素执行操作
toArray():将 Stream 转换为数组
reduce(BinaryOperator):将 Stream 中的元素进行归约操作,如求和、求积等
collect(Collector<T, A, R>):将 Stream 转换为其他数据结构,如 List、Set、Map 等
min(Comparator) 和 max(Comparator):求 Stream 中的最小值和最大值
count():计算 Stream 中的元素个数
anyMatch(Predicate)、 allMatch(Predicate)和 noneMatch(Predicate):测试 Stream 中的元素是否满足条件

总结

    总之,函数式编程应该用在抽象层次高、复用多的场景,而不是单纯业务逻辑的简单使用,所以使用之前也需要考虑代码的可维护性,不要为了使用而使用。

参考资料

https://segmentfault.com/a/1190000023747150?u_atoken=32fee544-f7cd-4df8-91ce-47d87de22668&u_asession=01BkaeBGWvPsHgIxJuFpnoNGPxJQJySVIG_gUH0Zhzyl3BspF4RLjnbbfCiqcVJce3Q_ZsrrQOL_dSA1zPPreegtsq8AL43dpOnCClYrgFm6o&u_asig=05Y3_7uvSQPfeIm07fP1vQdsj7FudfwePFM1eZA8ckEX67ToyquOZSHt9Eq1dLJNVc2ZcteDCchFNXGVEWv7Bg9exkOToHgrvdg9F4V3tl_h37OAllOeO5m6VUw3FfBfuvCzxkbE7J_MY1C7r2si7g5w-u3Ek8VtswbP6brvtqZqAsaf4GixaBYwLCG6zwc6qqksmHjM0JOodanL5-M1Qs1VR0BeJHCUjPk5wHVLCzDY_-BDXafsOvV_V2XawGnXUfPxqotRqV_sByrLqI5UuuDXcloE9fupmv8OEkmKJFEyPUpLHxH1iRKZmnjAu0Zefw&u_aref=hlmwq%2BeFAhcbCWMBRwoz8hv0WME%3D
https://xie.infoq.cn/article/047263a6c694daa5d65295b25
https://blog.51cto.com/u_16213587/7346422
https://zhuanlan.zhihu.com/p/629299261
https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng%3D%3D&chksm=fa4a5cf5cd3dd5e312c496bc67b600c4234d48f068d4b94c1924c9f5aee59b51466302ec4301&idx=2&mid=2247541572&scene=27&sn=1384374f96a83de90fc2d9625bce7707&utm_campaign=geek_search&utm_content=geek_search&utm_medium=geek_search&utm_source=geek_search&utm_term=geek_search#wechat_redirect
https://pdai.tech/md/develop/refactor/dev-refactor-if-else.html

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

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

相关文章

图片剪切软件,让图片处理更加高效

随着数字技术的不断发展&#xff0c;图片剪切也在不断进步。从手动裁剪到自动识别&#xff0c;图片剪切正变得越来越简单、高效&#xff0c;在这里面&#xff0c;图片处理软件的作用是不可忽视的。 所需工具&#xff1a; 一个【首助编辑高手】软件 需要剪切的图片 操作步骤…

【python】魔术方法大全(一)--基础篇

如果对你有帮助&#xff0c;欢迎微信搜索【海哥python】关注这个互联网苟且偷生的工具人。 什么是魔术方法 所谓魔法方法&#xff0c;它的官方的名字实际上叫special method&#xff0c;是Python的一种高级语法&#xff0c;允许你在类中自定义函数&#xff0c;并绑定到类的特殊…

Weblogic安全漫谈(四)

黑名单机制必然会推动两种研究方向的发展&#xff1a;一是挖掘不在黑名单的新组件&#xff0c;是为绕过规则&#xff1b;二是发掘检查的盲区&#xff0c;是为绕过逻辑。 CVE-2020-14756 二次反序列化具有对抗检查逻辑的天生丽质&#xff0c;在CVE-2018-2893中就有利用字节数组…

Kubeadmin实现k8s集群:

Kubeadmin来快速搭建一个k8s集群&#xff1a; 二进制搭建适合大集群&#xff0c;50台以上的主机&#xff0c; 但是kubeadm更适合中小企业的业务集群 环境&#xff1a; Master&#xff1a;20.0.0.71 2核4G 或者4核8G docker kubelet kubectl flannel Node1&#xff1a;20.…

面试题:vue2中option API的和vue3中composition API中的数据和方法能否交互?

结论&#xff1a; vue2中option API中的数据和方法可以从vue3中的composition API中进行调用&#xff0c; 而vue3中的composition API是不可以从vue2中的数据进行调用。 原理&#xff1a; 因为composition API中的函数setup在页面的生命周期中要比vue2中option API中的data、…

MR实战:词频统计

文章目录 一、实战概述二、提出任务三、完成任务&#xff08;一&#xff09;准备数据1、在虚拟机上创建文本文件2、上传文件到HDFS指定目录 &#xff08;二&#xff09;实现步骤1、创建Maven项目2、添加相关依赖3、创建日志属性文件4、创建词频统计映射器类5、创建词频统计归并…

六、Spring 声明式事务

本章概要 声明式事务概念 编程式事务声明式事务Spring事务管理器 基于注解的声明式事务 准备工作基本事务控制事务属性&#xff1a;只读事务属性&#xff1a;超时时间事务属性&#xff1a;事务异常事务属性&#xff1a;事务隔离级别事务属性&#xff1a;事务传播行为 6.1 声…

phpstudy_pro 关于多版本php的问题

我在phpstudy中安装了多个PHP版本 我希望不同的网站可以对应不同的PHP版本&#xff0c;则在nginx配置文件中需要知道不同的PHP版本的监听端口是多少&#xff0c;如下图所示 然而找遍了php.ini配置&#xff0c;并未对listen进行设置&#xff0c;好奇是怎么实现不同的PHP监听不同…

AI交互提示工程指南技术

简述: 当今互联网行业对于AI提示工程的需求日益增长,而《AI提示工程指南》是一本旨在满足这种需求的宝贵指南。本指南由一位对AI提示工程充满热情并自学而来的互联网从业者撰写,旨在为行业人员提供一个全面、易懂的参考手册。 这本指南将引领您踏上AI提示工程的旅程,深入探…

【Linux】Linux Page Cache页面缓存的原理

Page cache&#xff08;页面缓存&#xff09;是计算机操作系统中的一种机制&#xff0c;用于将频繁访问的数据从磁盘存储到内存中&#xff0c;以便更快地访问。当程序从磁盘请求数据时&#xff0c;操作系统会检查该数据是否已经存在于页面缓存中。如果存在&#xff0c;数据可以…

猫咪主食冻干K9、希喂、SC生骨肉冻干哪款好?详细对比测评这三款产品

随着科学养猫的观念深入人心&#xff0c;越来越多的铲屎官开始关注猫咪主食的营养与健康。主食冻干&#xff0c;作为一种模拟猫咪原始猎食的食品&#xff0c;因其高营养保留而受到广大猫奴的喜爱。相比传统的膨化猫粮&#xff0c;主食冻干更符合猫咪的饮食天性&#xff0c;提供…

【Storm实战】1.1 图解Storm的抽象概念

文章目录 0. 前言1. Storm 中的抽象概念1.1 流 (Stream)1.2 拓扑 (Topology)1.3 Spout1.4 Bolt1.5 任务 (Task)1.6 工作者 (Worker) 2. 形象的理解Storm的抽象概念2.1 流 (Stream)2.2 拓扑 (Topology)2.3 Spout2.4 Bolt2.5 任务 (Task)2.6 工作者 (Worker)场景1场景2 3.参考文档…

如何打造家居产业数字化转型范式?林氏家居以数智供应链作答

近年来&#xff0c;我国房地产行业逐步进入深度调整期。作为下游产业&#xff0c;家居家装行业的发展也来到了新阶段。业内人士指出&#xff0c;新房市场成交规模收缩&#xff0c;家居家装企业们开始整合资源&#xff0c;向存量房市场、产品科技化以及数字化转型。 国家层面出…

教学/直播/会议触摸一体机定制_基于展锐T820安卓核心板方案

触控一体机是一种集先进的触摸屏、工控和计算机技术于一体的设备。它取代了传统的键盘鼠标输入功能&#xff0c;广泛应用于教学、培训、工业、会议、直播、高新科技展示等领域。触摸一体机的应用提升了教学、会议和展示的互动性和信息交流。 触摸一体机方案基于国产6nm旗舰芯片…

设置进程优先级

#include <windows.h>int main() {// 获取当前进程的句柄HANDLE hProcess GetCurrentProcess();// 设置当前进程的优先级为高SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS);// 执行其他代码return 0; }进程优先级 标志 idle &#xff08;低&#xff09; IDL…

Python (十七) __name__ == ‘__main__‘ 作用

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一波电子书籍资料&#xff0c;包含《Effective Java中文版 第2版》《深入JAVA虚拟机》&#xff0c;《重构改善既有代码设计》&#xff0c;《MySQL高性能-第3版》&…

小学副科老师轻松吗

在小学里&#xff0c;除了语文、数学和英语这些主科&#xff0c;还有许多副科老师&#xff0c;他们的工作日常是什么样的呢&#xff1f;今天&#xff0c;让我们一起来揭秘小学副科老师的一天。 备课&#xff1a;在忙碌中寻找创意的火花 副科老师同样需要花费大量时间进行备课…

视频剪辑指南:如何将多个视频快速批量合并的方法

在日常生活和工作中&#xff0c;经常要将多个视频片段合并为一个完整的视频。但是手动剪辑每个视频不仅费时&#xff0c;而且效率低下。那么如何解决这个问题呢&#xff0c;可以采用一些快速批量合并视频的方法。现在一起来看看云炫AI智剪如何批量合并视频的具体步骤吧。 合并…

Windows 使用 nmap软件测试 UDP 端口

下载windows版nmap &#xff0c;下载后双机默认安装。 Download the Free Nmap Security Scanner for Linux/Mac/Windows 打开CMD &#xff0c; 输入 cd C:\Program Files (x86)\Nmap C:\Program Files (x86)\Nmap>ncat -z -v -u ntp.aliyun.com 123 Ncat: Version 7.80 ( …

《现代C++语言核心特性解析》笔记(三)

二十四、三向比较&#xff08;C20&#xff09; 1. “太空飞船”&#xff08;spaceship&#xff09;运算符 C20标准新引入了一个名为“太空飞船”&#xff08;spaceship&#xff09;的运算符 <>&#xff0c;它是一个三向比较运算符。<> 之所以被称为“太空飞船”运…