请注意这篇文章并不是教你怎么使用Java8里面的集合新特性–stream流,也不是教会你怎么使用lambda表达式,它纯粹是我个人从函数式编程思想的角度出发,来思考我们为什么需要函数式编程以及何时才需要函数式编程的一个思考过程。
先说说我在开发过程中碰到的一个问题,因为有问题才能驱动人去解决问题嘛!这个问题就是我在写不同的定时任务的时候,发现每次都要使用一段相同的for循环代码段,中间唯一不同的就是在for循环里面执行的一部分代码段。这部分代码段根据不同的需求而不同,而使用这个for循环的目的就是为了遍历一个列表。那部分不同的代码就是要在for循环里面根据列表里面不同的值来执行相应的任务。
所以写了很多这种定时任务之后我就感觉那部分for循环代码能不能抽成一个函数啊,这样就能复用了啊。但是不幸的的是,我要执行的不同的代码段是在for循环里面,而不是在for循环后面。所以要想以前一样,把这个for循环提取出来是不起作用的,因为照以前的思想函数的参数只能是变量,不能是代码段。我在没有想到函数式编程思想的时候甚至想能不能有一种搭积木式的编程方式,就是只要一个函数里面包含for循环的前一个大括号,而在另一个函数里面包含了一个后大括号,然后两个函数按顺序排放,是不是就能正确执行了?但是很遗憾,Java并没有提供这种“诡异”的编程方式。
不过,从我或多或少对函数式编程的理解,我在想我是不是可以利用函数式编程思想来改造一下。因为我那些根据不同业务而不同的代码段其实也可以写成一个函数(参数就是for循环中被遍历列表中的值),然后把这个函数传进那个for循环构成的函数。也就是把函数B当做另一个函数A的参数传进去,这样函数套函数,就像俄罗斯套娃一样。
于是我上面的想法用Java可以这样来写:
先定义一个接口,这个接口被@FunctionalInterface修饰,表示这是一个函数式接口。@FunctionalInterface注解起着一个限制作用,它使得这个接口拥有且只能只能拥有一个方法,为什么限定只能拥有一个方法,我们后面就知道了。
@FunctionalInterface
public interface MyFunction {
void doSomething(Integer num);
}
定义一个函数functionA,形参传入上面定义的函数式接口:
void functionA (List<Integer> numList, MyFunction functionB) {
for (Integer num : numList) {
functionB.doSomething(num);
}
}
实现上面的函数接口:
MyFunction myFunction = (num) -> {
System.out.println(num);
};
这样使用的话直接就可以把myFunction传进functionA了。
我们来试验一下:
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
MyFunction myFunction = (num) -> {
System.out.println(num);
};
functionA(list, myFunction);
}
结果就是:
1
2
3
4
再来说一下为什么被@FunctionalInterface注解修饰的接口只能有一个方法,这是因为有两个函数签名不一样的函数将导致你实现函数接口的时候,编译器不知道你是要用哪个函数。因为箭头函数是匿名的。
不知道通过这个小例子有没有帮助你理解函数式编程思想呢?其实只要记住一点,就是函数也可以当做参数传进一个函数里面,不再仅限于变量传进一个函数了。还有就是我们要擅于把不变的部分抽成一个函数,把变化的部分也抽象成一个函数式的接口,然后根据不同的需求来做具体的实现。这样你的代码的复用性就很高了。
我们可以思考一下为什么这样写复用性就会变高,这背后的原理是什么?函数式编程其实是一种代码组织方式的进步。这是因为我们可以把一段代码,也就是一系列操作传到一个函数中。于是我们就可以编写这样一个带有执行这一系列操作的函数,通过传入不同的参数,达到执行不同操作的目的。
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!