了解函数式编程的实际应用场景以及优点。
文章目录
- 什么是函数式编程
- 函数式编程的使用
- 原理解析
什么是函数式编程
以数学中的函数作为切入点,只关注参数之间的运算满足某种规则,例如z=x+y。
那么如何体现在编程中呢,熟知的function定义可以作为参考,我们定义一个add方法,传入两个int参数x和y做和计算并输出结果:
public int add(int a, int b) {
return a + b;
}
接下来我们直接看函数式编程如何实现:
BinaryOperator<Integer> binaryOperator = (x, y) -> x + y;
没错,定义一个BinaryOperator函数一行就能解决,就是如此优美(不过需要注意的是jdk8及以上才支持这种写法)。
带着欣赏的角度,我们继续深入理解函数式编程的魅力。
函数式编程的使用
除了上面我们距离的输入两个参数,输出一个结果的BiFunction函数,java还内置了许多函数:
接口 | 输入参数 | 返回类型 | 说明 |
---|---|---|---|
Predicate | T | boolean | 断言函数,对入参做判断是否符合条件 |
Consumer | T | / | 消费函数,消费一个数据,无出参 |
Function<T, R> | T | R | 普通函数 |
Supplier | / | T | 生产函数,生产一个数据,无入参 |
UnaryOperator | T | T | 一元函数(对应一个输入),输出和输入类型一致 |
BiFunction<T, U, R> | (T,U) | R | 2个输入与输出均为不同类型参数的函数(也可以类型一致) |
BinaryOperator | (T, T) | T | 二元函数(对应两个输入),输出和输入类型一致,即上面举例的z=x+y |
- 定义函数
// 断言函数,判断int x是否大于0
Predicate<Integer> predicate = x -> x > 0;
// 消费函数,对Object object做点什么
Consumer<Object> consumer = object -> System.out.println("对object做些什么...");
// 普通函数
Function<Integer, String> function = x -> "x的值,x=" + x;
// 生产函数,生产一个Object数据/对象
Supplier<Object> supplier = () -> new Object();
// 一元函数,输入和输出类型均为Integer,并对输入的值做和运算
UnaryOperator<Integer> unaryOperator = x -> x + x;
// 2个输入与输出均不同类型的函数,此处输入的两个参数类型保持一致
BiFunction<Integer, Integer, String> biFunction = (x, y) -> "x+y=" + x + y;
// 二元函数,输入和输出类型均为Integer,并对输入的值做和运算
BinaryOperator<Integer> binaryOperator = (x, y) -> x + y;
- 调用
BinaryOperator<Integer> binaryOperator = (x, y) -> x + y;
binaryOperator.apply(1, 2);
优点已经不言而喻了,非常简洁明了,这是关于函数式编程的脑图,可以作为参考:
原理解析
那么函数式编程是怎么实现的呢。
我们以BinaryOperator二元函数作为入口,查看究竟是如何达到函数式编程的优美:
观察源码,我们能够发现BinaryOperator其实是一个添加了@FunctionInterface注解并继承了BiFunction的泛型接口,其中包含了两个实现的方法:
- minBy()
- maxBy()
而BiFunction其实也是一个接口,包含了两个方法,其中apply()是未实现的方法:
- apply()
- addThen()
此时我们再回到我们的函数定义,实际上我们则是定义了一个实现apply()方法的实例。
BinaryOperator<Integer> binaryOperator = (x, y) -> x + y;
关键信息来了,敲重点:
函数式接口是只有一个抽象方法的接口,只要接口中有且仅有一个方法没有被实现,那么这个接口就可以被看做是一个函数式接口。
基于此概念,我们也可以定义属于我们自己的函数式接口,例如此处我们定义一个MyFunction的接口,并只预留一个未实现的方法doSomething。
@FunctionalInterface
public interface MyFunction<T> {
/**
* 做些什么...
* @param t
*/
T doSomething(T t);
}
public static void main(String[] args) {
MyFunction<Integer> myFunction = x -> x;
Integer integer = myFunction.doSomething(1);
// 1
System.out.println(integer);
}
而加上@FunctionalInterface注解的意义在于,标记了该接口是要作为函数接口,需要保证满足有且仅有一个未实现的方法,如果这个时候我们定义超过1个未实现的方法, 编译器则会提示我们报错Multiple non-overriding abstract methods:
参考资料:
- java函数式编程Function(java函数式编程实战)
- 一文带你入门 Java 函数式编程