Java编码技巧系列之「“函数式编程“原来代码可以这么整洁 」

在这里插入图片描述

介绍


什么是函数式编程

函数式编程是一种编程范式,即一切都是数学函数。在Java面向对象编程中,程序是一系列相互作用(方法)的对象,而在函数式编程中,程序会是一个无状态的函数组合序列。

概念看不懂很正常,我们来看一个例子:

1、定义一个“函数接口”

/**
 * 自定义`函数接口`
 *
 * @author: mock
 * @date: 2023-01-21 14:09:48
 */
@FunctionalInterface
public interface DogFunction {   
    void doAction(String action);   
}

2、使用接口函数

/**
 * 使用接口函数
 *
 * @author: mock
 * @date: 2023-01-21 14:17:17
 */
public class DogFunctionDemo {

    public static void main(String[] args) {

        //函数Lambda表达式,从这里就可以看到函数编程的特点
        DogFunction eat = action -> {
            System.out.println("执行动作" + action);
            System.out.println("吃东西");
        };

        DogFunction run = action -> {
            System.out.println("执行动作" + action);
            System.out.println("跑步");
        };

        doAction(eat,"1");
        doAction(run,"2");
    }

    /**
     * `DogFunction` 将函数接口作为参数传入
     */
    public static void doAction(DogFunction action, String step) {
        action.doAction(step);
    }
}

用法

@FunctionalInterface自定义函数接口注解

Java 8为函数式接口引入了一个新注解@FunctionalInterface,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。

示例

// 注解定义一个函数接口
@FunctionalInterface 
public interface Person {
    void sayHello(String msg); 
}

// 使用Lambda表达式定义函数,将函数直接作为变量输出
Person personSay = message -> System.out.println("Hello " + message);
personSay.sayHello("`mock`")

Function 函数接口

JDK1.8新特性,传入一个参数,返回一个值的函数。例如stream.map()方法。

大家可以看下源码:

@FunctionalInterface
public interface Function<T, R> {
 
    //接收一个参数,返回一个值
    R apply(T t);

    //函数链式执行
    //接收一个函数,执行函数,并且把该函数返回值作为参数执行`apply`,最终返回一个函数
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }
  
    //函数链式执行 和`compose差不多` 
    //区别是先执行了函数的`apply`方法后再执行参数`apply`
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    //示例化一个接口对象本身
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

单看方法解释和源码,是不是一头雾水?那我们看看下面的例子就能懂啦

/**
 * 使用`Function`实现类
 *
 * @author: Mock
 * @date: 2022-05-25 22:47:28
 */
public class FunctionDemo {

    public static void main(String[] args) {
        //Function 可作为一个方法参数
        FunctionTest functionTest = new FunctionTest();

        //1.执行函数方法 `apply`
        functionMethod1(value -> value + 1);

        //2.执行函数方法 `compose`
        functionMethod2(
                value -> {
                    System.out.println("执行opt1");
                    return value + 1;
                },
                value -> {
                    System.out.println("执行opt2");
                    return value + 2;
                }
        );

        //3.执行函数方法 `compose`
        functionMethod3(
                value -> {
                    System.out.println("执行opt1");
                    return value + 1;
                },
                value -> {
                    System.out.println("执行opt2");
                    return value + 2;
                }
        );

    }

    /**
     * 执行函数方法`apply`
     *
     * @param opt 函数
     */
    private static void functionMethod1(Function<Integer, Integer> opt) {
        System.out.println("开始执行:functionMethod1");
        System.out.println("functionMethod1 执行结果: " + opt.apply(1));
        System.out.println();
    }

    /**
     * 执行函数方法`compose`
     *
     * @param opt1 函数1
     * @param opt2 函数2
     */
    private static void functionMethod2(
            Function<Integer, Integer> opt1,
            Function<Integer, Integer> opt2
    ) {
        System.out.println("开始执行:functionMethod2");
        System.out.println("functionMethod2 执行结果: " + opt1.compose(opt2).apply(3));
        System.out.println();
    }

    /**
     * 执行函数方法`andThen`
     *
     * @param opt1 函数1
     * @param opt2 函数2
     */
    private static void functionMethod3(
            Function<Integer, Integer> opt1,
            Function<Integer, Integer> opt2
    ) {
        System.out.println("开始执行:functionMethod2");
        System.out.println("functionMethod3 执行结果: " + opt1.andThen(opt2).apply(3));
        System.out.println();
    }

}

输出结果

可以看到 :
functionMethod1执行apply,接收一个参数,执行并且返回
functionMethod2执行compose,先执行opt2再执行opt1
functionMethod3执行andThen,先执行opt1再执行opt2

image.png

BiFunction函数接口

JDK1.8新特性,传入两个参数(第一个参数可以是上个参数返回值),返回一个返回值

大家接着看一下源码

@FunctionalInterface
public interface BiFunction<T, U, R> {

    //接收两个参数,返回一个值
    R apply(T t, U u);
   
    //函数链,根函数执行结果做为after的参数
    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }
    
}

那么大家可能会问了,为什么BiFunction里面没有compose方法。

根据Function接口里面的compose方法,我们可以分析出,它是先执行参数的apply方法,在执行调用者的apply方法。假如BiFunction里面有compose,又因为BiFunction接受两个参数,那么通过上面的分析,可以得出,它的compose方法的参数必定是BiFunction类型的,但是下一步执行调用者的apply方法的时候,因为只有一个参数,所以是无法满足的。所以BiFunction里面没有compose方法。

其他一些函数接口

    BiFunction<T,U,R>:代表了一个接受两个输入参数的方法,并且返回一个结果

    DoubleFunction<R>:代表接受一个double值参数的方法,并且返回结果

    DoubleToIntFunction:接受一个double类型输入,返回一个int类型结果。

    DoubleToLongFunction:接受一个double类型输入,返回一个long类型结果

    IntFunction<R>:接受一个int类型输入参数,返回一个结果 。

    IntToDoubleFunction:接受一个int类型输入,返回一个double类型结果 。

    IntToLongFunction:接受一个int类型输入,返回一个long类型结果。

    LongFunction<R>: 接受一个long类型输入参数,返回一个结果。

    LongToDoubleFunction: 接受一个long类型输入,返回一个double类型结果。

    LongToIntFunction:接受一个long类型输入,返回一个int类型结果。

    ToDoubleBiFunction<T,U>:接受两个输入参数,返回一个double类型结果

    ToDoubleFunction<T>:接受一个输入参数,返回一个double类型结果

    ToIntBiFunction<T,U>:接受两个输入参数,返回一个int类型结果。

    ToIntFunction<T>:接受一个输入参数,返回一个int类型结果。

    ToLongBiFunction<T,U>:接受两个输入参数,返回一个long类型结果。

    ToLongFunction<T>:接受一个输入参数,返回一个long类型结果。

小牛试刀一下


上面说了那么多,给大伙看一个计算器的实现示例,看看函数式编程的应用

/**
 * 计算器枚举
 *
 * @author: Mock
 * @date: 2023-05-28 15:27:49
 */
@Getter
@AllArgsConstructor
public enum Calculator {

    /**
     * 加法
     */
    ADDITION((v1, v2) -> v1 + v2),

    /**
     * 减法
     */
    SUBTRACTION((v1, v2) -> v1 - v2),

    /**
     * 乘法
     */
    MULTIPLICATION((v1, v2) -> v1 * v2),

    /**
     * 除法
     */
    DIVISION((v1, v2) -> v1 / v2);

    /**
     * 计算器函数方法
     */
    private BiFunction<Double, Double, Double> computer;

    public static void main(String[] args) {
        Double v1 = 15D;
        Double v2 = 3D;

        System.out.println("执行加法 v1 + v2 = :" + Calculator.ADDITION.getComputer().apply(v1,v2));
        System.out.println("执行减法 v1 - v2 = :" + Calculator.SUBTRACTION.getComputer().apply(v1,v2));
        System.out.println("执行乘法 v1 * v2 = :" + Calculator.MULTIPLICATION.getComputer().apply(v1,v2));
        System.out.println("执行除法 v1 / v2 = :" + Calculator.DIVISION.getComputer().apply(v1,v2));
    }

}

执行结果

image.png

从例子中可以看到,我们很方便的在枚举中,将函数做为成员属性定义给枚举,减少了很多代码,且一目了然。
在很多编码场景都可以利用函数这种方式去实现。

博客求赞.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杭州水果捞|Java毕业设计成品

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值