我们经常提到,Java8是革命性的一个版本,原因就是正式引入了函数式编程,那Java的函数式编程在实际应用中到底有什么用呢?结合实际的应用,我整理出了函数式在Java的几个经典用途。

缓求值

惰性求值(Lazy evaluation)是在需要时才进行求值的计算方式。惰性求值自然地在数据结构中包含递归,可以以简单的方式表示无限的概念,这种方式有利于程序的模块化。

在Java这种Strict语言中,我们定义了临时变量代码块就会立即运算并且取得结果,而实际上很多结果未必会使用到,就存在不必要的计算,如下面的代码

/**
* 是否是标准或默认工作台
* 理论上仅判断默认工作台即可(因为标准就是默认),此处是兜底一下历史逻辑
*
* @param context
* @return
*/
public boolean isStandardOrDefaultWorkbench(SchemaContext context) {
Supplier<Boolean> isDefaultWorkbench = () -> StringUtils.isNotBlank(context.getDefaultAppUuid())
&& StringUtils.equals(context.getAppUuid(), context.getDefaultAppUuid());
return WorkbenchType.WORKBENCH.equals(context.getWorkbenchType()) || isDefaultWorkbench.get();
}

当我们使用临时变量定义的时候,需要理解计算出代码

StringUtils.isNotBlank(context.getDefaultAppUuid())
&& StringUtils.equals(context.getAppUuid(), context.getDefaultAppUuid())

的值,并用于后面的判断,不管下面的代码是否为True,我们都消耗了一次计算

WorkbenchType.WORKBENCH.equals(context.getWorkbenchType())

而我们使用了Supplier,就可以定义一个匿名表达式,只有当前面判断为False的时候,才会执行

isDefaultWorkbench.get()

,而当前面判断为True的时候,就不会执行后面的代码了,通过缓求值的方式,可以节省在某些情况下的消耗,可以提升系统性能,节省资源。

高阶函数

高阶函数是指使用其他函数作为参数、或者返回一个函数作为结果的函数。

我们常用的Stream就是典型的流处理思想,可以通过Stream来处理集合并执行相关的操作,其中Stream包含了大量的高阶函数,我们可以直接使用匿名表达式来传递业务逻辑。值得注意,匿名表达式本身返回的就是函数,因此匿名表达式就是一种高阶函数。

 // 过滤隐藏应用
return appDetails.stream().filter(app -> {
AppModel appModel = new AppModel(app.getAppId(), app.getAppType());
return !appDisplayModel.getHideApps().contains(appModel);
}).collect(Collectors.toList());

从上面可以逻辑可以看到,我们可以通过集合的Stream对象进行filter、map、collect操作,其中这些高阶函数就可以传递lambda表达式,用于处理我们需要的逻辑。

当然除了Stream之外,Java很多工具类也支持外部传入lambda,比如还有一种常见的工具类Optional。

    /**
* 获取组织默认工作台appUuid
* @param corpId
* @param orgConfigTag
* @return
*/
public String getDefaultWorkbenchAppUuid(String corpId, OrgConfigTag orgConfigTag) {
return Optional.ofNullable(orgConfigTag).map(OrgConfigTag::getDefaultAppUuid)
.orElseGet(() -> OpenPageUtils.buildWorkbenchAppUuid(corpId));
}

其中的orElseGet方法就支持外部传入lambda表达式。

函数回调

在计算机程序设计中,回调函数,或简称回调(Callback 即call then back 被主函数调用运算后会返回主函数),是指通过参数将函数传递到其它代码的,某一块可执行代码的引用。 这一设计允许了底层代码调用在高层定义的子程序。

函数回调可用于接口两块完全无关的逻辑,比如在A模块执行完毕后,执行B模块,B模块的代码事先在A模块注册。很多框架型的逻辑,比如统一结果处理、缓存框架、分布式锁、线程缓存等都可以通过这个方式来优化,就可以使用lambda的方式来实现。核心的逻辑就是先处理相关的通用的中间件逻辑,然后通过lambda来执行业务逻辑。

/**
* 创建我的页面
*
* @param corpId
* @param uid
*/
protected void createMyPage(String corpId, Long uid, String appUuid, Method method) throws WorkbenchException {
// 并发锁控制
distributeLockSupport.lockAndDo(key,
() -> {
//创建页面方法
userCorpPageSupport.createMyPage(corpId, uid);
return null;
}
);
}

如上面的代码,对于要实现分布式锁控制的模块,可以使用lambda的回调,来实现加锁和业务逻辑的分离。其中的定义如下述代码所示

/**
* 基于缓存的分布式锁操作
*
* @param key 资源key
* @param callback 回调函数
* @param <T> 返回结果类型
* @return
* @throws Exception
*/
public <T> T lockAndDo(String key, Callback<T> callback) throws WorkbenchException {
//取锁
... try {
//加锁
...
return callback.process();
} catch (WorkbenchException ex) {
throw ex;
} finally {
try {
//释放锁
...
} catch (Exception ex) {
//异常处理
}
}
} //回调接口定义,供外部传入lambda
@FunctionalInterface
public interface Callback<T> {
/**
* 回调处理
*
* @return 处理结果
* @throws WorkbenchException
*/
T process() throws WorkbenchException, ServiceException;
}

自定义函数

从函数式编程的核心思想来说,函数是一等公民,而面向对象的核心是封装。可以通过定义函数的方式来降低面向对象命令式编程的复杂度。即尽量把处理逻辑都抽象成可复用的函数,并通过lambda的方式进行调用。

/**
* 批量操作
*
* @param consumer 函数式写法,对批量对象操作
*/
public void batchProccess(BiConsumer<Long, OrchardDTO> consumer) {
if(orchardDTOMap!=null && orchardDTOMap.size()>0){
for (Map.Entry<Long, OrchardDTO> entry : orchardDTOMap.entrySet()) {
consumer.accept(entry.getKey(), entry.getValue());
}
} }
public void syncPush(MicroAppContext context, BatchOrchardDTO batchOrchardDTO) {
//传入lambda来执行逻辑
batchOrchardDTO.batchProccess((k, v) -> pushFacadeService.push(k, v));
}

如上图所示,就是在类中定义了一个高阶函数,在调用的时候从外部传入lambda表达式,从而实现batchProccess和pushFacadeService.push两个方法的解耦。上面的核心和集合支持lambda是一个意思,也就是把需要外部频繁操作的部分抽象出来定义成函数式接口,以供外部传入不同的lambda进行丰富的操作。

总结

Java8非常重要的就是引入了函数式编程的思想,使得这门经典的面向对象语言有了函数式的编程方式。弥补了很大程度上的不足,函数式思想在处理复杂问题上有着更为令人称赞的特性。

Java8函数式编程应用的更多相关文章

  1. 关于Java8函数式编程你需要了解的几点

    函数式编程与面向对象的设计方法在思路和手段上都各有千秋,在这里,我将简要介绍一下函数式编程与面向对象相比的一些特点和差异. 函数作为一等公民 在理解函数作为一等公民这句话时,让我们先来看一下一种非常常 ...

  2. Java8 函数式编程详解

    Java8 函数式编程详解 Author:Dorae Date:2017年11月1日23:03:26 转载请注明出处 说起Java8,可能很多人都已经知道其最大的改进,就是引入了Lambda表达式与S ...

  3. Java8函数式编程探秘

    引子 将行为作为数据传递 怎样在一行代码里同时计算一个列表的和.最大值.最小值.平均值.元素个数.奇偶分组.指数.排序呢? 答案是思维反转!将行为作为数据传递. 文艺青年的代码如下所示: public ...

  4. [2017.02.23] Java8 函数式编程

    以前学过Haskell,前几天又复习了其中的部分内容. 函数式编程与命令式编程有着不一样的地方,函数式编程中函数是第一等公民,通过使用少量的几个数据结构如list.map.set,以及在这些数据结构上 ...

  5. [一] java8 函数式编程入门 什么是函数式编程 函数接口概念 流和收集器基本概念

      本文是针对于java8引入函数式编程概念以及stream流相关的一些简单介绍 什么是函数式编程?   java程序员第一反应可能会理解成类的成员方法一类的东西 此处并不是这个含义,更接近是数学上的 ...

  6. 漫漫人生路,学点Jakarta基础-Java8函数式编程

    接口默认方法 Java8版本以后新增了接口的默认方法,不仅仅只能包含抽象方法,接口也可以包含若干个实例方法.在接口内定义实例方法(但是注意需要使用default关键字) 在此定义的方法并非抽象方法,而 ...

  7. Java8函数式编程的宏观总结

    1.java8优势通过将行为进行抽象,java8提供了批量处理数据的并行类库,使得代码可以在多核CPU上高效运行. 2.函数式编程的核心使用不可变值和函数,函数对一个值进行处理,映射成另一个值. 3. ...

  8. java8函数式编程实例

    什么是函数式编程 函数式编程是java8的一大特色,也就是将函数作为一个参数传递给指定方法.别人传的要么是基本数据类型,要么就是地址引用 ,我们要穿一个“动作”. Stream 说到函数式编程,就不得 ...

  9. Java8函数式编程以及Lambda表达式

    第一章 认识Java8以及函数式编程 尽管距离Java8发布已经过去7.8年的时间,但时至今日仍然有许多公司.项目停留在Java7甚至更早的版本.即使已经开始使用Java8的项目,大多数程序员也仍然采 ...

  10. 重识Java8函数式编程

    前言 最近真的是太忙忙忙忙忙了,很久没有更新文章了.最近工作中看到了几段关于函数式编程的代码,但是有点费解,于是就准备总结一下函数式编程.很多东西很简单,但是如果不总结,可能会被它的各种变体所困扰.接 ...

随机推荐

  1. C++笔记(自用)

    <Effective C++> 条款11 在operator=中处理"自我赋值" 自我赋值 证同测试: if(this==&rhs)return*this; 影 ...

  2. C#中的ConcurrentExclusiveSchedulerPair类

    为什么使用ConcurrentExclusiveSchedulerPair? 现实生活中的例子是一个停车场的入口和出口,多辆车可以同时进入和离开停车场,但是只有一个车辆可以进入或离开一次. 这时候就需 ...

  3. Visual Studio 2022 设置代码补全

    Visual Studio 2022 设置代码补全 VS默认使用 Tab 键进行代码补全. 若要使用回车补全需要重新设置,具体路径如下: ​ 工具----选项----文本编辑器----C/C++--- ...

  4. 「sdoi2019 - D2T2」移动金币

    对 @command_block 没有 implementation 做法的细化.理论来说可以通过,但因为我实现得较劣无法通过.:( 把金币中的空隙看作石子,就是一个阶梯 Nim 的模型(有总共 \( ...

  5. Solution Set -「CF 1534」

    这 1+2? 「CF1534 A」Colour the Flag Link. 把 W / R 拉出来广搜,注意判断全空的情况. #include <bits/stdc++.h> using ...

  6. 解决CentOS 7出现docker-compose: command not found

    解决CentOS 7出现docker-compose: command not found 1. 安装docker-compose 既然使用了docker-compose那自然得安装了 在GitHub ...

  7. 2023-10-04:用go语言,现有一棵无向、无根的树,树中有 n 个节点,按从 0 到 n - 1 编号 给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges , 其中 edge

    2023-10-04:用go语言,现有一棵无向.无根的树,树中有 n 个节点,按从 0 到 n - 1 编号 给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges , 其中 edge ...

  8. fmpeg 多张图片转视频,视频转gif,截取视频,截取视频里的帧

    ffmpeg 多张图片转视频,视频转gif,截取视频,截取视频里的帧 月色下的独轮车 于 2017-06-20 17:32:01 发布 6250 收藏 3文章标签: ffmpeg版权 android学 ...

  9. nacos-mysql.sql

    https://github.com/alibaba/nacos/blob/master/distribution/conf/nacos-mysql.sql # Nacos安装 /home/nacos ...

  10. 是因为不同的浏览器内核吗--Could not register service workers到底是怎么回事

    什么是浏览器内核 浏览器内核(Rendering Engine),是浏览器最核心的部分. 它负责处理网页的HTML.CSS.JavaScript等代码,并将其转化为可视化的网页内容.即我们常说的对网页 ...