文章目录
什么是函数式编程
函数式编程是种编程方式,并不是Java8的新特性,只是Java8内置了很多函数式编程的类,它将电脑运算视为函数的计算。将业务逻辑细化,抽象,封装成一个个功能函数,并借助语言自带的高阶函数api,将整个业务流程转化为函数之间的相互调用,这就是函数式编程。
与命令式编程相比,函数式编程更加强调做什么,在函数式编程中我们只需要关注做什么样的事情,不需要关注具体的实现细节。
例子:查找一个数组中的最小值;
import java.util.stream.IntStream;
public class MainMain{
public static void main(String[] args) throws Exception {
int[] nums = {33,55,-55,90,-666,90};
// 命令式编程
int min = Integer.MAX_VALUE;
for (int i : nums){
if (min > i){
min = i;
}
}
System.out.println("命令式编程实现:" + min);
// 函数式编程实现
int asInt = IntStream.of(nums).min().getAsInt();
System.out.println("函数式编程实现:" + asInt);
}
}
说明:
使用命令式编程,需要用程序告诉计算机我们每一步需要做什么最终才能实现我们想要的结果;
如果我们使用函数式编程我们只需要关注我们最终要实现的结果即可,而且从代码量的角度来看,也简洁了不少;
什么是函数式接口
它指的是有且只有一个未实现的方法的接口,一般通过@FunctionalInterface
这个注解来表明某个接口是一个函数式接口。
函数式编程入门
Java8中函数式编程语法能够精简代码。
使用Consumer作为示例,它是一个函数式接口,包含一个抽象方法accept,这个方法只有输入而无输出。
现在我们要定义一个Consumer对象,传统的方式是这样定义的:
Consumer c = new Consumer() {
@Override
public void accept(Object o) {
System.out.println(o);
}
};
而在Java8中,针对函数式编程接口,可以使用Lambda
表达式定义:
Consumer c = (o) -> {
System.out.println(o);
};
上面已说明,函数式编程接口都只有一个抽象方法,因此在采用这种写法时,编译器会将这段函数编译后当作该抽象方法的实现。
如果接口有多个抽象方法,编译器就不知道这段函数应该是实现哪个方法的了。
语法说明:
=
后面的函数体我们就可以看成是accept函数的实现
。- 输入(入参):
->
前面的部分,即被()包围的部分。此处只有一个输入参数,实际上输入是可以有多个的,如两个参数时写法:(a, b);当然也可以没有输入,此时直接就可以是()。 - 函数体:->后面的部分,即被{}包围的部分;可以是一段代码。
- 输出(返回值):函数式编程可以没有返回值,也可以有返回值。如果有返回值时,需要代码段的最后一句通过return的方式返回对应的值。
当函数体中只有一个语句时,可以去掉{}进一步简化:
Consumer c = (o) -> System.out.println(o);
然而这还不是最简的,由于此处只是进行打印,调用了System.out中的println静态方法对输入参数直接进行打印,因此可以简化成以下写法:
Consumer c = System.out::println;
它表示的意思就是针对输入的参数将其调用System.out中的静态方法println进行打印。
JDK8中新增的函数式接口
Consumer
Consumer是消费的意思,即针对某个东西我们来使用它,因此它包含有一个有输入而无输出的accept接口方法;
除accept方法,它还包含有andThen这个方法;
其定义如下:
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
可见这个方法就是指定在调用当前Consumer后是否还要调用其它的Consumer;
使用示例:
public static void consumerTest() {
Consumer f = System.out::println;
Consumer f2 = n -> System.out.println(n + "-F2");
//执行完F后再执行F2的Accept方法
f.andThen(f2).accept("test");
//连续执行F的Accept方法
f.andThen(f).andThen(f).andThen(f).accept("test1");
}
Function
Function是函数的意思,而函数经常是有输入输出的,因此它含有一个apply方法,包含一个输入与一个输出;
除apply方法外,还有可以和其他函数组合的默认方法compose
与andThen
及indentity
。
使用示例:
@Test
public void test() {
Function<Integer, Integer> f1 = s -> {
System.out.println("f1...run...");
return s++;
};
Function<Integer, Integer> f2 = s -> {
System.out.println("f2...run...");
return s * 2;
};
/**
* 下面表示在执行compose时,先执行f2,后执行f1, 并且f2的输出当作f1的输入。
* 相当于以下代码:
* Integer a = f2.apply(1)
* Integer b = f1.apply(a)
*/
Function<Integer, Integer> compose = f1.compose(f2);
System.out.println(compose.apply(1));
System.out.println("---黄金分割线---");
// 面表示在执行andThen时,先执行f1,后执行f2, 并且f1的输出当作f2的输入。
Function<Integer, Integer> andThen = f1.andThen(f2);
System.out.println(andThen.apply(1));
System.out.println("---黄金分割线---");
// identity方法会返回一个不进行任何处理的Function,即输出与输入值相等;
System.out.println(Function.identity().apply("hello"));
}
运行结果:
Predicate
Predicate是断定意思,判断某个东西是否满足某种条件; 因此它包含test方法,根据输入值来做逻辑判断,其结果为True或者False。
使用示例:
@Test
public void test() {
Predicate<String> p1 = o -> o.equals("test");
Predicate<String> p2 = o -> o.startsWith("t");
System.out.println(p1.test("test"));
// negate: 做取反处理
System.out.println(p1.negate().test("test"));
// and: 针对同一输入值,多个Predicate均返回True时返回True,否则返回False;
System.out.println(p1.and(p2).test("test"));
// or: 针对同一输入值,多个Predicate只要有一个返回True则返回True,否则返回False
System.out.println(p1.or(p2).test("test"));
}
函数式编程接口的使用
通过Stream以及Optional两个类,可以进一步利用函数式接口来简化代码。
3.2 Optional
用于简化Java中对空值的判断处理,以防止出现各种空指针异常。
Optional实际上是对一个变量进行封装,它包含有一个属性value,实际上就是这个变量的值。
3.2.1 Optional对象创建
它的构造函数都是private类型的,因此要初始化一个Optional的对象无法通过其构造函数进行创建。它提供了一系列的静态方法用于构建Optional对象:
3.2.1.1 empty
用于创建一个空的Optional对象;其value属性为Null。
如:
Optional o = Optional.empty();
3.2.1.2 of
3.2.1.3 ofNullable
3.2.2 方法
3.2.3 使用场景
常用的使用场景如下:
3.2.3.1 判断结果不为空后使用
如某个函数可能会返回空值,以往的做法:
String s = test();
if (null != s) {
System.out.println(s);
}
现在的写法就可以是:
Optional<String> s = Optional.ofNullable(test());
s.ifPresent(System.out::println);
乍一看代码复杂度上差不多甚至是略有提升;那为什么要这么做呢?
一般情况下,我们在使用某一个函数返回值时,要做的第一步就是去分析这个函数是否会返回空值;如果没有进行分析或者分析的结果出现偏差,导致函数会抛出空值而没有做检测,那么就会相应的抛出空指针异常!
而有了Optional后,在我们不确定时就可以不用去做这个检测了,所有的检测Optional对象都帮忙我们完成,我们要做的就是按上述方式去处理。
3.2.3.3 变量为空时提供默认值
如要判断某个变量为空时使用提供的值,然后再针对这个变量做某种运算;
以往做法:
if (null == s) {
s = "test";
}
System.out.println(s);
现在的做法:
Optional<String> o = Optional.ofNullable(s);
System.out.println(o.orElse("test"));
3.2.3.3 变量为空时抛出异常,否则使用
以往写法:
if (null == s) {
throw new Exception("test");
}
System.out.println(s);
现在写法:
Optional<String> o = Optional.ofNullable(s);
System.out.println(o.orElseThrow(()->new Exception("test")));
参考:
https://blog.csdn.net/icarusliu/article/details/79495534
https://blog.csdn.net/icarusliu/article/details/79504602