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.

相关文章
|
20天前
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
38 6
Spring Boot 入门:简化 Java Web 开发的强大工具
|
16天前
|
监控 架构师 Java
Java虚拟机调优的艺术:从入门到精通####
本文作为一篇深入浅出的技术指南,旨在为Java开发者揭示JVM调优的神秘面纱,通过剖析其背后的原理、分享实战经验与最佳实践,引领读者踏上从调优新手到高手的进阶之路。不同于传统的摘要概述,本文将以一场虚拟的对话形式,模拟一位经验丰富的架构师向初学者传授JVM调优的心法,激发学习兴趣,同时概括性地介绍文章将探讨的核心议题——性能监控、垃圾回收优化、内存管理及常见问题解决策略。 ####
|
21天前
|
存储 Java 数据挖掘
Java 8 新特性之 Stream API:函数式编程风格的数据处理范式
Java 8 引入的 Stream API 提供了一种新的数据处理方式,支持函数式编程风格,能够高效、简洁地处理集合数据,实现过滤、映射、聚合等操作。
35 6
|
1月前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
1月前
|
Java API
Java中的Lambda表达式与函数式编程####
【10月更文挑战第29天】 本文将深入探讨Java中Lambda表达式的实现及其在函数式编程中的应用。通过对比传统方法,我们将揭示Lambda如何简化代码、提高可读性和维护性。文章还将展示一些实际案例,帮助读者更好地理解和应用Lambda表达式。 ####
|
1月前
|
分布式计算 Java API
Java 8引入了流处理和函数式编程两大新特性
Java 8引入了流处理和函数式编程两大新特性。流处理提供了一种声明式的数据处理方式,使代码更简洁易读;函数式编程通过Lambda表达式和函数式接口,简化了代码书写,提高了灵活性。此外,Java 8还引入了Optional类、新的日期时间API等,进一步增强了编程能力。这些新特性使开发者能够编写更高效、更清晰的代码。
33 4
|
1月前
|
Java 大数据 API
14天Java基础学习——第1天:Java入门和环境搭建
本文介绍了Java的基础知识,包括Java的简介、历史和应用领域。详细讲解了如何安装JDK并配置环境变量,以及如何使用IntelliJ IDEA创建和运行Java项目。通过示例代码“HelloWorld.java”,展示了从编写到运行的全过程。适合初学者快速入门Java编程。
|
1月前
|
Java API 开发者
Java中的Lambda表达式与函数式编程####
在Java的演变过程中,Lambda表达式和函数式编程的引入无疑是一次重大的飞跃。本文将深入探讨Lambda表达式的定义、用法及优势,并结合实例说明如何在Java中利用Lambda表达式进行函数式编程。通过对比传统编程方式,揭示Lambda表达式如何简化代码、提高开发效率和可维护性。 ####
|
1月前
|
存储 安全 Java
🌟Java零基础-反序列化:从入门到精通
【10月更文挑战第21天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
80 5
|
1月前
|
安全 Java 调度
Java中的多线程编程入门
【10月更文挑战第29天】在Java的世界中,多线程就像是一场精心编排的交响乐。每个线程都是乐团中的一个乐手,他们各自演奏着自己的部分,却又和谐地共同完成整场演出。本文将带你走进Java多线程的世界,让你从零基础到能够编写基本的多线程程序。
36 1

热门文章

最新文章

下一篇
DataWorks