• 函数式编程总结与应用


    前言

    什么是函数式编程,我们由一段代码来引出

    Optional<Integer> result = Stream.of("foo", "bar", "hello")
                .map(s->s.length())
                .filter(f->f<=3)
                .max((o1,o2)->o1-o2);
            System.out.println(result.get());
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这段代码的作用是过滤出字符长度小于3的字符串,并计算出最大的字符串长度。java为函数式编程引入了3个新的语法概念:Stream类、Lambda表达式和函数接口(functional interface);其中

    • Stream类的作用是通过它支持用“.”级联多个函数操作的代码编写方式;
    • Lambda表达式的作用是简化代码的编写;
    • 函数接口的作用是让我们可以把函数包裹成函数接口,把函数当作参数一样使用。

    Stream类

    假如我们要计算表达式:(3-1)x2+5。如果按我们知道的函数来编写代码,你会这样写:

    add(multiply(subtract(3,1),2),5)
    
    • 1

    可读性不是太好,可以换个更好的写法,如下:

    subtract(3,1).multiply(2).add(5)
    
    • 1

    我们知道java中的.代表的是调用关系,即某个对象调用了某个方法。为了支持上面这种级联调用,我们让每个函数返回一个通用类型,Stream类对象。中间操作返回的仍是Stream对象,终止操作返回确定的结果值。

    再看下我们上边的方法

    Optional<Integer> result = Stream.of("foo", "bar", "hello")
                .map(s->s.length())// map返回Stream对象
                .filter(f->f<=3) // filter返回Stream对象
                .max((o1,o2)->o1-o2);//max终止操作,返回Optional
            System.out.println(result.get());
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Lambda表达式

    Lambda表达式的作用是简化代码的编写,是函数式编程的核心,表达式即匿名内部类。以上边的代码举个例子,比如用map函数的使用方式:

    Stream.of("foo", "bar", "hello")
                .map(new Function<String, Integer>() {
                    @Override
                    public Integer apply(String s) {
                        return s.length();
                    }
                });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    简化后的代码就是

    Stream.of("foo", "bar", "hello").map(s->s.length())
    
    • 1

    Lambda表达式主要有三部分:输入,函数体和输出,标准写法如下:

    (a,b) -> {语句1;语句2;return 输出;} //a和b都是输入参数,

    还可以简化,如果入参只有一个,那么可以省略(),直接写成“a->{…}”;如果没有入参,可以直接保留函数体;

    还有一点需要注意:java8会自动地将在Lambda表达式中使用的局部变量视为final,看下边的例子:

    如果是类成员变量的话,没有问题
    在这里插入图片描述

    函数接口

    特征

    1. interface接口类
    2. 类中有且仅有1个public类型的接口方法;
    3. @FunctionalInterface注解标识可选,加上会提高可读性。

    JDK1.8的支持

    JDK源码 java.util.function包下面提供的一系列的预置的函数式接口定义

    接口类功能描述
    Runnable直接执行一段处理函数,无任何输出参数,也没有任何输出结果。
    Supplier执行一段处理函数,无任务输入参数,返回一个T类型的结果。与Runnable的区别在于Supplier执行完之后有返回值。
    Consumer执行一段处理函数,支持传入一个T类型的参数,执行完没有任何返回值。
    BiConsumer与Consumer类型相似,区别点在于BiConsumer支持传入两个不同类型的参数,执行完成之后依旧没有任何返回值。
    Function执行一段处理函数,支持传入一个T类型的参数,执行完成之后,返回一个R类型的结果。与Consumer的区别点就在于Function执行完成之后有输出值。
    BiFunction与Function相似,区别点在于BiFunction可以传入两个不同类型的参数,执行之后可以返回一个结果。与BiConsumer也很类似,区别点在于BiFunction可以有返回值。
    UnaryOperator传入一个参数对象T,允许对此参数进行处理,处理完成后返回同样类型的结果对象T。继承Function接口实现,输入输出对象的类型相同。
    BinaryOperator允许传入2个相同类型的参数,可以对参数进行处理,最后返回一个仍是相同类型的结果T。继承BiFunction接口实现,两个输入参数以及最终输出结果的对象类型都相同。
    Predicate支持传入一个T类型的参数,执行一段处理函数,最后返回一个布尔类型的结果。
    BiPredicate支持传入2个相同类型T的参数,执行一段处理函数,最后返回一个布尔类型的结果。

    函数式接口应用

    比如我们现在需要统计系统的资源利用率,但是数据的来源不一样,获取到数据后进行组装,然后入库,大致的流程如下:

    主流程
    public void  calculateResourceUsage(int type, ResourceObtain<T> resourceObtain) {
            // 调用函数式接口获取源数据
            List<ResourceUsage> list = resourceObtain.obtainResource(type);
            // 组装list
            buildData(list);
            // 保存list
            saveData(list);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    函数式接口
    @FunctionalInterface
    public interface ResourceObtain<T> {
        List<T> computePrice(int type);
    }
    
    • 1
    • 2
    • 3
    • 4
    调用

    比如从平台1上边获取数据

    resourceUsageService.calculateResourceUsage(1,x ->{
                //调用平台1接口获取平台1的数据
                List<ResourceUsage> list = rpc1.get();
                return list;
            });
    
    • 1
    • 2
    • 3
    • 4
    • 5

    获取平台2数据

    resourceUsageService.calculateResourceUsage(2,x ->{
                //调用平台1接口获取平台1的数据
                List<ResourceUsage> list = rpc2.get();
                return list;
            });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    总结
    • 其实上边的函数,也可以换成接口实现类的方式,这个可以自己在开发过程中进行评估,函数式有自己的高效,如果逻辑不多的话,可以使用函数式接口,因为不用再创建太多的类,如果逻辑复杂点的话,可以再创建接口和对应的实现。

    • 函数式接口算是在程序运行过程中对接口进行回调

  • 相关阅读:
    【数据结构】单链表之--无头单向非循环链表
    121. 买卖股票的最佳时机 --力扣 --JAVA
    有趣的算法(七) ——快速排序改进算法
    Oracle EBS 部署JSP
    3D打印服务展示预约小程序的效果如何
    Opus Security利用其AI驱动的多层次优先级引擎提升漏洞管理能力
    Elasticsearch 8.11 中的合并更少,摄取更快
    centos7修改系统运行级别
    typescript44-对象之间的类兼容器
    Centos Linux 7 查看网卡
  • 原文地址:https://blog.csdn.net/buyaoshuohua1/article/details/126334473