写接口也不单单是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> | T | boolean | 断言,常用于filter过滤 |
Consumer<T> | T | 无 | 仅消费数据,无输出,就是消费者 |
Function<T, R> | T | R | 输入T,输出R |
Supplier<T> | 无 | T | 无输入,输出T,就是生产者 |
UnaryOperator<T> | T | T | 输入和输出类型相同 |
BiFunction<T, U, R> | T, U | R | 输入两个参数,输出R |
BinaryOperator<T> | T, T | T | 输入两个参数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;