命令式编程关注对过程,也就是怎么做是回答 3W 的问题( When、Why 和 How)
但是在函数式编程的世界里关注是做什么,也就是回答 what 的问题
-
可描述:应该关注描述什么是你想要而不是如何得到想要的
pure-water-e1314289675967.jpg
-
确定性:(也就是纯)根据输入可以每次得到相同(确定的)输出。这个好处是不言而喻,确定性好处就是便于测试。
-
不可变:不可变,修改一个变量不是修改原有的变量而是赋值一份新的变量然后在此基础上进行修改。
函数式编程的好处
- 可读性,对于函数并不陌生,一个一个简单函数更适合我们去阅读哪些充满黑科技(侧边效应)的代码逻辑。
- 代码更加优雅
一个最简单需求就是我们从数组中找到最小数,这样逻辑出现在每一个应用中,我们在熟悉不过了,拿来就写。但是我们平时每天都做的是并不一定是最佳方法。先用命令式编程方式实现一下。
public static void main(String[] args) {
int[] courses = {1,2,3,5,6,7};
int min = Integer.MAX_VALUE;
for (int i:courses
) {
if(i < min){
min= i;
}
}
}
看起来有点复杂,有人会惊讶这还复杂呀,多简单。您说简单是因为这种方式是你熟悉的,所以认为这种方法简单,其实熟悉并非简单。这里首先产生中间变量 min 这对结果是没有用处的。
System.out.println(min);
int min2 = IntStream.of(courses)
.min().getAsInt();
System.out.println(min2);
如果这里数据有上亿条,通过 for 循环是扛不住这么大数据,我们就需要多线程并行来处理数据,我们首先对数据划分区域,然后每一个区域由一个线程来计算其最小值,最后进行合并找出其最小值,但是对于 java8 的流处理我们只需要将 parallel()
就可以实现上面效果,我们不需要自己实现,新的 api 已经为我们实现这些。
int min2 = IntStream.of(courses).parallel()
.min().getAsInt();
System.out.println(min2);
创建线程是 java 并发编程中不可缺少也是最基本的 coding。我们创建了 Thread 然后接受一个匿名函数做出参数,其中有一个 run 方法是我们在新开的线程中进行的计算。
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("ok");
}
}).start();
}
lambda 表达式在 java 中写法。
new Thread(()-> System.out.println("ok")).start();
我们看一看 Runnable 这是一个接口,接口中有 run 方法,这个方法没有输入也没有输出,不过 runanble 是一个接口的示例,lambda 表达式其实就是就是返回指向对象接口的对象实例。
Runnable target = () -> System.out.println("ok");
new Thread(target).start();
将刚刚代码重构一下就看 lambda 更加清晰了。
interface Func {
int doubleNum(int i);
}
我们可以写成
Func fun1 = (i) -> i * 2;
Func fun2 = i -> i * 2;
Func fun3 = (int i) -> {
return i * 2;
};
我们知道 lambda 是返回一个实现接口的实例,但是对什么样接口可以作为 lambda 返回值类型是有限制的。我们的接口只能有一个方法。这个应该很好理解,函数接口
通过注解@FunctionalInterface
来修饰接口,表示这个接口为函数式接口,在编译期进行来检查该接口是否符合函数式接口。
@FunctionalInterface
interface Func {
int doubleNum(int i);
}
th-3.jpeg
单一原则是程序设计的原则,所以我们尽量将接口进行细化拆分。在 java 的 JDK8 中引进了 default 可以添加一些接口自身已经默认实现的方法。
@FunctionalInterface
interface Func {
int doubleNum(int i);
default int add(int x, int y){
return x + y;
}
}
System.out.println(fun1.add(2,3));
我们将接口进行拆分后,可以通过接口继承实现多个接口组合。
@FunctionalInterface
interface Func {
int doubleNum(int i);
default int add(int x, int y){
return x + y;
}
}
@FunctionalInterface
interface FuncA {
int doubleNum(int i);
default int add(int x, int y){
return x + y;
}
}
@FunctionalInterface
interface FuncB extends Func,FuncA{
@Override
default int add(int x, int y) {
return Func.super.add(x,y);
}
}
在多个接口组合情况下,如果继承的多个接口都有一个同名的默认方法,我们的接口就需要显式地指定使用哪个接口方法或对该方法进行复写。
网友评论