Java函数式编程

196 阅读4分钟

写接口也不单单是CURD,也可以来点高级的,讲响应式编程之前需要先看下函数式编程

函数式编程的概念

采用系统自带的函数和Lambda表达式进行编程

举一个简单的例子,求一个int数组的最小值

// 定义一个int数组
int[] nums = {1, 2, 3, 4, 5};

int min = Integer.MAX_VALUE;
for (int i : nums) {
    if (min > i) {
        min = i;
    }
}
System.out.println(min);

上面是传统的方式,代码很简单,再看一下函数式编程的代码

System.out.println(IntStream.of(nums).min().getAsInt());

就一行

首先从代码上就能体现出来函数式编程的优点,代码易读(先转成流,再求最小值,再取出最小值)

如果现在nums数组很大,采用传统的方式,单线程for循环效率不能达到要求,首先想到的就是将nums数组分组后放到线程池中去处理,最后再拿出各个分组的最小值,再求一次最小值得到结果

采用流和函数式编程的方式,可以将代码改成如下:

System.out.println(IntStream.of(nums).parallel().min().getAsInt());

只是添加了.parallel()就将流变成了并行流,自动的分组并多线程处理,优势是很明显的

函数接口

不用再定义Interface接口,直接用自带的Function<T, R>接口即可

对于Function<T, R>接口,查看源码,该接口添加了@FunctionalInterface注解,这个注解的作用是接口内只能定义一个待实现的方法,default方法不算在内,既然接口只有一个方法,自然的就想到Lambda表达式中对于一个方法的接口实现会更简洁,同时也符合设计模式中的单一职责模式

源码中Function<T, R>接口定义了一个R apply(T t);方法,由此可以知道:T表示入参类型,R表示出参类型,这个方法就是我们要使用的,举一个简单的例子来演示

public static void main(String[] args) {
    int num = 123;
    // 调用下面定义的test方法,传入一个函数和要处理的数据
    String b = test(a -> String.valueOf(a), num);
    System.out.println(b);
}

// 定义一个方法,方法的第一个参数是一个函数,第二个参数是要处理的数据
public static String test(Function<Integer, String> func, int a) {
    // 调用函数的apply方法,传入参数a
    return func.apply(a);
}

Function<Integer, String> func表示该函数的apply方法入参是Integer,出参是String,func是一个函数,String b = test(a -> String.valueOf(a), num);这样调用实际上就是Function接口的匿名实现

自带的函数接口

以上介绍了自带的Function接口,JDK中自带了其他几种常用的函数接口

接口
入参返回
说明
Predicate<T>Tboolean断言,常用于filter过滤
Consumer<T>T仅消费数据,无输出,就是消费者
Function<T, R>TR输入T,输出R
Supplier<T>T无输入,输出T,就是生产者
UnaryOperator<T>TT输入和输出类型相同
BiFunction<T, U, R>T, UR输入两个参数,输出R
BinaryOperator<T>T, TT输入两个参数T,输出T,三个数据类型相同

这些都只是接口,大家可以查看源码,找到接口定义

简单的示例:

// 定义一个断言,入参是Integer,函数体是入参是否小于0,返回布尔值
Predicate<Integer> a = i -> i < 0;
System.out.println(a.test(123));

方法引用

也是为了简化代码,有以下引用方法

静态方法引用

Function<Integer, String> func = a -> String.valueOf(a);
System.out.println(func.apply(123));

上面的a -> String.valueOf(a)中,valueOf是一个静态方法,可以简写为

Function<Integer, String> func = String::valueOf;
System.out.println(func.apply(123));

非静态方式引用

public static void main(String[] args) {
    Test02 test02 = new Test02();
    Function<Integer, String> func = test02::b;
    System.out.println(func.apply(123));
}

public static class Test02 {
    public String b(Integer a) {
        return String.valueOf(a);
    }
}

构造函数引用

public static void main(String[] args) {
    // 无参构造
    Supplier<Test02> s1 = Test02::new;
    System.out.println(s1.get());

    // 有参构造
    Function<Long, Test02> s2 = Test02::new;
    // apply方法传入有参构造的参数
    System.out.println(s2.apply(1L));
}

@Data
public static class Test02 {
    private Long id;

    public Test02() {
    }

    public Test02(Long id) {
        this.id = id;
    }
}

级联表达式 / 高阶函数

以下的函数称为高阶函数

Function<Integer, Function<Integer, Integer>> func = a -> b -> a + b;

// 调用高阶函数
System.out.println(func.apply(1).apply(2));

对于a -> b -> a + b这样的函数可以这么理解:

a是入参,出参是一个函数b -> a + b,调用时func.apply(1)的返回结果是函数b -> a + b,所以调用func.apply(1)后可以继续调用.apply(2)

调用.apply(2)b是入参,出参是a + b,因此最终实际上就是1+2

为什么要这么麻烦的写呢?

目的是将多个参数的函数转变为一个参数的函数

如这样的代码:

x -> y -> z = x + y + z;