Java-Function函数式编程-入门

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: Java-Function函数式编程-入门

Function函数介绍
我们在没深入了解Function函数式相关接口之前,可能只是在使用Stream流处理时,用过它的相关接口。有些同学也就止步于此,并没有深入了解过它的设计理念。
Function中文接口文档
Stream接口文档

对于Stream流大家常用的方法有哪些?

  1. filter(Predicate<? super T> predicate)// 条件筛选
  2. map(Function<? super T, ? extends R> mapper)// 对单个item对象转换操作
  3. flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)// 对item对象操作,并返回一个新的Stream流
  4. forEach(Consumer<? super T> action)// 循环遍历
  5. anyMatch(Predicate<? super T> predicate)// 条件筛选,整个集合item有一个为true
  6. allMatch(Predicate<? super T> predicate)// 条件筛选,全部item为true
  7. reduce(BinaryOperator accumulator);// 累计计算,值累加等
  8. collect(Collector<? super T, A, R> collector);// 集合操作,元素汇总转换计算
  9. 看着这些常用方法,入参都是Function相关接口函数

Function常用函数详解

Predicate函数

Predicate函数

传入一个参数,返回一个boolean值的函数,例如filter方法。
// strList是一个List. filter方法需要传入Predicate函数方法.
// 获取集合数量,并且集合不存在null字符串
long count1 = strList.stream().filter(item -> StringUtils.hasLength(item))
.count();
// 简写
long count2 = strList.stream().filter(StringUtils::hasLength).count();
// 自定义方法,理论上我们只要是一个 传入一个String类型参数并返回一个boolean参数的方法。都可以放入filter方法
long count3 = strList.stream().filter(item -> customHasLength(item)).count()
// 简写
long count4 = strList.stream().filter(FunctionDemoApplication::customHasLength).count();

/**

  • 自定义方法,字符串不为null或空字符串
    */
    public static boolean customHasLength(String str){
    return StringUtils.hasLength(str);
    }

Function函数
传入一个参数,返回一个值的函数。例如map方法。

public static void main(String[] args) {
List planList = new ArrayList<>(Collections.emptyList());
planList.add(new Plan(1L,"SUCCEED"));
planList.add(new Plan(2L,"FAIL"));

// map方法入参,需要传入一个Function函数。item -> item.getPlanNo())写法属于
// Function函数规范。item表示入参,item.getPlanNo()表示返参
Set<Long> planNoList1 = planList.stream().map(item -> item.getPlanNo()).collect(Collectors.toSet());
// 简写
Set<Long> planNoList2 = planList.stream().map(Plan::getPlanNo).collect(Collectors.toSet());

}

static class Plan{
private Long planNo;
private String planStatus;

public Plan(Long planNo, String planStatus){
    this.planNo = planNo;
    this.planStatus = planStatus;
}

public Long getPlanNo() {
    return planNo;
}
public Plan setPlanNo(Long planNo) {
    this.planNo = planNo;
    return this;
}
public String getPlanStatus() {
    return planStatus;
}
public Plan setPlanStatus(String planStatus) {
    this.planStatus = planStatus;
    return this;
}

}
Consumer函数
这是一个传入一个参数,无返回值的函数。例如forEach方法。

public static void main(String[] args) {
List planList = new ArrayList<>();
planList.add(new Plan(1L,"SUCCEED"));
planList.add(new Plan(2L,"FAIL"));
planList.add(new Plan(3L,"SUCCEED"));

List<Long> planNo = new ArrayList<>();
planList.stream()
        .filter(plan -> "SUCCEED".equals(plan.getPlanStatus()))
        // forEarch方法入参,需要传入一个Consumer函数。plan -> planNo.add(plan.getPlanNo())符合函数规范。 plan表示入参。
        .forEach(plan -> planNo.add(plan.getPlanNo()));
System.out.println("count:" + planNo.size());

}
BiFunction函数
与Function区别在于,BiFunction允许传入2个参数,返回一个值。

BiConsumer函数
与Consumer函数区别在于,BiConsumer函数允许传入2个参数,无返回值。

BinaryOperator函数
这是BiFunction的一个子接口,BiFunction可以传入2个参数,返回一个值。BinaryOperator则是传入的2个参数和返回的值类型必须一致,所以用在reduce函数方法入参时,就可以用来进行值累加的操作。例如传入BigDecimal::add,Long::sum,甚至于BigDecimal::max,用来获取最大值。

总结一句话,Function函数作为方法入参,核心原因就是把具体业务逻辑交由外层处理。使方法只处理核心逻辑,从而减少业务逻辑对方法的侵入性,使代码更加简洁优雅,易于维护。
例如map(Function<? super T, ? extends R> mapper)方法,就是把集合中单个item对象的处理逻辑交由外部逻辑处理。 map方法内只需获取结果进行核心逻辑处理。
image.png

再例如filter(Predicate<? super T> predicate)方法,把具体校验逻辑交由外部处理,filter方法内只需获取结果进行核心逻辑处理。
image.png

实际场景Function函数应用
当我了解Stream对Function的函数运用后,我佩服这些源码编写大佬的思路与才华。所以我认为核心是需要学习他们的编码设计思路,在日常编写代码中,真的碰到了类似场景,就可以想起并运用这套思想去设计代码,使得我们代码更加优雅,简洁高效。这也是我们学习源码的初衷。

例如有这样一个场景,我们对接了很多支付渠道,每家对接口参数的加密方式不同。我们可以针对支付渠道设计一个抽象类。在抽象类中定义一个签名方法。具体不同渠道的签名规则以Function函数传入。

抽象类中公共签名方法

/**

  • 排序并签名
    *
  • @param params 要签名参数
  • @param append 待签名字符串追加内容
  • @param signName 签名参数
  • @param signer 签名函数
  • @return 最终签名
    */
    protected final String sign(Map params,

                         String append,
                         String signName,
                         Function<String, String> signer) {
    

    String signStr = params.keySet().stream()

         .filter(key -> StringUtils.hasLength(key)
                 && !key.equals(signName)
                 && StringUtils.hasLength(params.get(key)))
         .sorted()
         .map(key -> key + "=" + params.get(key))
         .collect(Collectors.joining("&"));
    

    signStr += append;

    if (logger.isDebugEnabled()) logger.debug("待签名字符串:{}", signStr);
    return signer.apply(signStr);
    }

/**

  • md5 大写加密
    */
    protected final String md5Upper(String signStr) {
    return md5Origin(signStr).toUpperCase();
    }

/**

  • md5 加密
    */
    protected final String md5Origin(String signStr) {
    return DigestUtils.md5Hex(signStr);
    }
    具体支付渠道进行签名调用

// 调用抽象类中的sign签名方法
String sign = sign(params, "&key=" + "私钥字符串", "sign"
// 这里不同渠道类中也可自定义加密规则
, this::md5Origin);

// 另外一种写法
String sign2 = sign(params, "&key=" + "私钥字符串", "sign"
, (signStr) -> DigestUtils.md5Hex(signStr));

类似的场景其实有很多,在很多源码使用上,都会把业务逻辑通过函数式入参,交由你来处理。使得源码方法的公用性会更强,代码更加简洁易于维护。
以上便是我对于Function函数入门的介绍,如果要更加深入的了解与运用,需要对源码贡献者的编程思路达成共鸣,才能一劳永逸。最后愿你我的头发茂盛。
End.

相关文章
|
16天前
|
Java
Java8函数式编程
Java8函数式编程
22 2
|
18天前
|
存储 安全 Java
从入门到精通:Java Map全攻略,一篇文章就够了!
【10月更文挑战第17天】本文详细介绍了Java编程中Map的使用,涵盖Map的基本概念、创建、访问与修改、遍历方法、常用实现类(如HashMap、TreeMap、LinkedHashMap)及其特点,以及Map在多线程环境下的并发处理和性能优化技巧,适合初学者和进阶者学习。
34 3
|
2天前
|
Java 大数据 API
14天Java基础学习——第1天:Java入门和环境搭建
本文介绍了Java的基础知识,包括Java的简介、历史和应用领域。详细讲解了如何安装JDK并配置环境变量,以及如何使用IntelliJ IDEA创建和运行Java项目。通过示例代码“HelloWorld.java”,展示了从编写到运行的全过程。适合初学者快速入门Java编程。
|
5天前
|
Java API 开发者
Java中的Lambda表达式与函数式编程####
在Java的演变过程中,Lambda表达式和函数式编程的引入无疑是一次重大的飞跃。本文将深入探讨Lambda表达式的定义、用法及优势,并结合实例说明如何在Java中利用Lambda表达式进行函数式编程。通过对比传统编程方式,揭示Lambda表达式如何简化代码、提高开发效率和可维护性。 ####
|
8天前
|
存储 安全 Java
🌟Java零基础-反序列化:从入门到精通
【10月更文挑战第21天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
37 5
|
6天前
|
安全 Java 调度
Java中的多线程编程入门
【10月更文挑战第29天】在Java的世界中,多线程就像是一场精心编排的交响乐。每个线程都是乐团中的一个乐手,他们各自演奏着自己的部分,却又和谐地共同完成整场演出。本文将带你走进Java多线程的世界,让你从零基础到能够编写基本的多线程程序。
18 1
|
12天前
|
Java 数据处理 开发者
Java多线程编程的艺术:从入门到精通####
【10月更文挑战第21天】 本文将深入探讨Java多线程编程的核心概念,通过生动实例和实用技巧,引导读者从基础认知迈向高效并发编程的殿堂。我们将一起揭开线程管理的神秘面纱,掌握同步机制的精髓,并学习如何在实际项目中灵活运用这些知识,以提升应用性能与响应速度。 ####
37 3
|
14天前
|
Java
Java中的多线程编程:从入门到精通
本文将带你深入了解Java中的多线程编程。我们将从基础概念开始,逐步深入探讨线程的创建、启动、同步和通信等关键知识点。通过阅读本文,你将能够掌握Java多线程编程的基本技能,为进一步学习和应用打下坚实的基础。
|
15天前
|
存储 安全 Java
从入门到精通:Java Map全攻略,一篇文章就够了!
【10月更文挑战第19天】本文介绍了Java编程中重要的数据结构——Map,通过问答形式讲解了Map的基本概念、创建、访问与修改、遍历方法、常用实现类(如HashMap、TreeMap、LinkedHashMap)及其特点,以及Map在多线程环境下的使用和性能优化技巧,适合初学者和进阶者学习。
39 4
|
15天前
|
Java
[Java]Socket套接字(网络编程入门)
本文介绍了基于Java Socket实现的一对一和多对多聊天模式。一对一模式通过Server和Client类实现简单的消息收发;多对多模式则通过Server类维护客户端集合,并使用多线程实现实时消息广播。文章旨在帮助读者理解Socket的基本原理和应用。
15 1