函数式编程及应用

目录

    • 什么是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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring国际化的应用及原理详解

1. 简介 Spring国际化&#xff08;Spring Internationalization&#xff0c;简称i18n&#xff09;是Spring框架提供的一种机制&#xff0c;用于支持多语言的应用程序。它使得开发者能够轻松地在应用程序中实现不同语言的支持&#xff0c;从而满足全球化的需求。通过Spring国际…

Selenium-java 定位元素时切换iFrame时的方法

具体方法如下图所示&#xff0c;如果iFrame中嵌套多层iFrame需要逐层定位到需要的那一层iFrame,完成操作后&#xff0c;执行该代码&#xff1a;driver.switchTo() .defaultContent() ; 是返回最顶部的frame

C语言编译器(C语言编程软件)完全攻略(第二部分:与编译器相关的几个知识点)

介绍常用C语言编译器的安装、配置和使用。 二、与编译器相关的几个知识点 上节我们介绍了编译器和 IDE 的概念&#xff0c;大家肯定希望赶紧实践一下&#xff0c;用 IDE 真正地运行一段C语言代码来看看效果&#xff0c;这样能够更快地获得成就感。 但是&#xff0c;使用 IDE …

网络故障问题一般性检查排查思路

一、基本连通性检查 在网络中ping是一个十分强大的TCP/IP工具。它可以用来检测网络的连通情况和分析网络速度、也可以ping网址根据域名得到服务器IP、同时我们根据ping返回的TTL值来判断对方所使用的操作系统及数据包经过路由器数量。 ping 网址&#xff0c;有几种输出情况&a…

开源项目go-admin的代码生成功能使用

go-admin代码生成功能 具体如何配置go-admin项目的可以查看首次使用go-admin进行配置启动go-admin&#xff0c;再看下一篇文章&#xff01; 找到代码生成 先进入到界面&#xff0c;点击开发工具&#xff0c;点击代码生成 代码生成操作 1. 创建表&#xff0c;可以使用可视…

【RK3399 PCIE调试——硬件信息资源获取】

一、1、 硬件接口 二、2、 PCB原理图 三、 官网地址&#xff1a; https://t.rock-chips.com/portal.php 相关资料和固件烧写可参考资料下载菜单

Linux GDB 调试

文章目录 一、Qemu二、Gdbvscode 调试 三、RootFs 一、Qemu qemu 虚拟机 Linux内核学习 Linux 内核调试 一&#xff1a;概述 Linux 内核调试 二&#xff1a;ubuntu20.04安装qemu Linux 内核调试 三&#xff1a;《QEMU ARM guest support》翻译 Linux 内核调试 四&#xff1a;…

半导体Memory的分类

文章目录 略图introRAM & ROM 略图 intro 存储器是嵌入式系统中用于存放数据和程序的模块。有些存储器是MCU内置的&#xff0c;有些是扩展的。 存储器嵌入式系统中常见且重要的外设模块。搞清楚存储器的分类是从事嵌入式开发工作的一项基本功。 从功能上&#xff0c;存储器…

利用小红书笔记详情API:为内容运营提供强大的支持

利用小红书笔记详情API&#xff0c;内容运营者可以获得对小红书平台上的笔记内容的深入洞察&#xff0c;从而为其运营工作提供强大的支持。以下是该API如何支持内容运营的几个关键方面&#xff1a; 获取笔记内容与数据&#xff1a; API允许内容运营者直接获取小红书平台上的笔记…

视频号小店开店需要准备什么资料?

我是电商珠珠 很多想要开通视频号小店的新手并不知道开通视频号小店需要准备什么资料。 今天我就来给大家详细的讲一下。 视频号小店分为三种类型&#xff0c;一种是普通企业店&#xff0c;一种是个体工商店&#xff0c;还有一种是品牌企业店。 先来说说普通企业店 1、营业…

面向对象编程(高级)

面向对象编程&#xff08;高级&#xff09; 1、类变量和类方法 &#xff08;1&#xff09; 概念 类变量&#xff0c;也称为静态变量&#xff0c;是指在类级别声明的变量。它们与特定类相关联&#xff0c;而不是与类的实例&#xff08;对象&#xff09;相关联。每个类变量只有…

【docker】cgroups资源限制

目录 一、cpu资源控制 1、 设置cpu使用率上限 2、设置cpu资源占用比&#xff08;设置多个容器时才有效&#xff09;Docker通过–cpu-shares指定cpu份额&#xff0c;默认为1024&#xff0c;值为1024的倍数。 3、设置容器绑定指定的CPU 三、内存资源控制 四、磁盘IO配额控制…

恭喜 Databend 上榜 2023 开源创新榜「优秀开源项目 」

近日&#xff0c;国家科技传播中心见证了一场开源界的重要事件&#xff1a;由中国科协科学技术传播中心、中国计算机学会、中国通信学会和中国科学院软件研究所联合主办&#xff0c;CSDN 承办的 2023 年开源创新榜专家评审会圆满落幕。由王怀民院士担任评委会主任&#xff0c;评…

MR实战:词频统计

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

如何搭建HomeAssistant智能家居管理平台并实现公网访问内网管理界面

目录 前言 一、下载HomeAssistant镜像 二、内网穿透HomeAssistant&#xff0c;实现异地控制智能家居 三、使用固定域名访问HomeAssistant 结语 前言 作者简介&#xff1a; 懒大王敲代码&#xff0c;计算机专业应届生 今天给大家聊聊如何搭建HomeAssistant智能家居管理平台…

数据结构之哈希——学习笔记

今天看网课学习了哈希的数据结构&#xff0c;写下这一篇博客记录自己的学习过程。 1.哈希简介&#xff1a; 我们发现某些时候映射到小集合的时候会同时有多个值映射到一个下标里面&#xff0c;所以接下来是这种情况的解决方案1&#xff1a; 我们考虑当两个数字映射之后的结果…

Python startswith()和endswith()方法及 index()方法:检测字符串中是否包含某子串

Python startswith()和endswith()方法 Python 字符串变量还可以使用 startswith() 和endswith() 方法。 startswith()方法 startswith() 方法用于检索字符串是否以指定字符串开头&#xff0c;如果是返回 True&#xff1b;反之返回 False。此方法的语法格式如下&#xff1a; …

向日葵远程工具的使用Mysql5.7的安装与配置

目录 一、向日葵远程安装与使用 二、Mysql 5.7 安装与配置 2.1 安装 2.2 Navicat Premium 12 测试连接 本机测试连接 外部访问MySQL测试连接 三、思维导图 一、向日葵远程安装与使用 简介&#xff1a; 向日葵远程控制是一款用于对远程PC进行管理和服务的软件,拥有5秒快速…

isEmpty 和 isBlank 的用法区别,居然一半的人答不上来.....

isEmpty 和 isBlank 的用法区别 isEmpty系列isBank系列 hi&#xff01;我是沁禹&#xff5e; 也许你两个都不知道,也许你除了isEmpty/isNotEmpty/isNotBlank/isBlank外,并不知道还有isAnyEmpty/isNoneEmpty/isAnyBlank/isNoneBlank的存在, come on ,让我们一起来探索org.apache…

网络调试 TCP,开发板用静态地址-入门4

用两台电脑&#xff08;无线网络&#xff09;做实验 1.1, 在电脑A上设置为Server如下&#xff1a; 选择TCP Server后&#xff0c;直接跳出用本机IP做为“本地主机地址” 1.2在 电脑B上设置为Client, 远程主机地址设置为Server的 IP 1.3, 在A, B两台电脑上能够互相发送数据 用…