首页 > 编程语言 >Java8函数式编程应用

Java8函数式编程应用

时间:2023-12-22 11:26:44浏览次数:37  
标签:逻辑 return 函数 编程 param context Java8 lambda

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

标签:逻辑,return,函数,编程,param,context,Java8,lambda
From: https://www.cnblogs.com/nengwenhuiwu/p/17920854.html

相关文章

  • SpringMVC处理Java8新日期类参数
    SpringMVC如何将request参数自动封装为LocalDate和LocalDateTime在使用SpringMVC时,java.util.Date类型字段可以使用@DateTimeFormat注解将application/x-www-from-urlencoded类型的请求中的字符串进行自动转换。而Java8中新的时间类型该如何支持呢?在application/x-www-from-u......
  • 无涯教程-Haskell - Nested if-else 语句函数
    以下代码显示了如何在Haskell中使用嵌套的if-else语句-main=doletvar=26ifvar==0thenputStrLn"Numberiszero"elseifvar`rem`2==0thenputStrLn"NumberisEven"elseputStrLn"NumberisOdd"在上面的示例中......
  • C++基础 -11- 类的构造函数
     ———————类的构造函数——————— ......
  • Map+函数式接口去掉if-else
    判断条件放在key中对应的业务逻辑放在value中这样子写的好处是非常直观,能直接看到判断条件对应的业务逻辑代码:importcom.wing.service.QueryGrantTypeService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.......
  • 实验7 文件应用编程
    实验任务41#include<stdio.h>2intmain(){3FILE*fp;4inti;5charch;6fp=fopen("data4.txt","r");7if(fp==NULL){8printf("failtoopenfile\n");9return1;10}1......
  • 无涯教程-Haskell - 函数组合
    功能组合是将一个功能的输出用作另一个功能的输入的过程,如果我们学习组成背后的数学会更好,在数学中,组成由f{g(x)}表示,其中g()是一个函数,其输出用作输入另一个功能,即f()。看下面的示例代码。在这里,我们使用函数组合来计算输入数字是偶数还是奇数。eveno::Int->Boolnoto......
  • rand函数--生成随机值
    #define_CRT_SECURE_NO_WARNINGS1#include<stdio.h>#include<stdlib.h>#include<time.h>#include<Windows.h>intmain(){ intret=0; srand((int)time(NULL));//避免多次重复调用,不然出现的数非常接近 while(1) { //rand库函数stdlib.h生成随机数--生成一个......
  • Excel-最强函数搭档INDEX&MATCH(指数+寻找)
    1、VLOOKUP函数的缺点有一个很大的缺点就是如果要搜寻的栏位不是在表格的最左侧,或是表格采用了横向排列的话,无用武之地。如果是横向表格,而要查询的资料行也恰巧是第一行的话,可以使用HLOOKUP函数,Hlookup与vlookup类似,只是查询方向不同=HOOKUP(被查询值,查询的范围,要传回的行数)困境......
  • Manacher与exKMP(扩展KMP,Z函数)
    Manacher算法该算法由GlennK.Manacher在1975年提出,首先注意到回文串的对称中心特性可能有所不同(中心可能为一个字符或者是在两个字符之间),那么我们将字母之间插入隔板,这两个回文串的对称中心就都在一个字符上了,suchas"|A|B|B|A|"、"|A|B|C|B|A|"过程对于一个回文串,有且......
  • 扩展 KMP/exKMP(Z 函数)
    模板链接QwQZ函数,又称扩展KMP(exkmp),可以\(O(n)\)求出一个字符串的所有后缀与这个字符串的LCP长度。怎么叫做扩展KMP但是前置知识没有KMP,Z函数的做法与Manacher有着异曲同工之妙,即存下了目前已扩展到的右端点最靠右端的后缀\(i\)与原串的LCP:\([i,i+Z[i]-1]\)......