Java的函数式编程详解

前言 虽然Jdk版本已经出到了20+,甚至IDEA创建maven默认项目Jdk版本都11了,但是你发任你发,我用Java8,最近别的组的同事要对接个需求,把代码

前言

虽然Jdk版本已经出到了20+,甚至IDEA创建maven默认项目Jdk版本都11了,但是你发任你发,我用Java8,最近别的组的同事要对接个需求,把代码仓库开给他之后反馈看不太明白代码。

用了这么久的Java8,我寻思这种话也好意思说出来吗,难道自己是PythonBoy出身就是看不懂Java的理由吗,身为一个合格的后端Boy不会还有人看不明白Java的函数式编程吧。

函数式编程

那什么是函数编程呢,说白了就是可以把函数作为参数传递给其他函数,亦或者将一个函数作为返回值从另一个函数中返回。

于是Jdk根据不同的场景提供了一些核心特性和函数式接口:

Consumer<T>:接受一个输入参数 T,并在方法内部执行操作,没有返回值。

Supplier<T>:不接受任何输入参数,提供一个结果 T。

Function<T, R>:接受一个输入参数 T,执行操作并返回一个结果 R。

Predicate<T>:接受一个输入参数 T,返回一个布尔值表示是否满足条件。

UnaryOperator<T>:接受一个输入参数 T,执行操作并返回一个与输入参数类型相同的结果。

BinaryOperator<T>:接受两个相同类型的输入参数 T,执行操作并返回一个相同类型的结果。

相信大家经常在业务代码中有过这样的场景,DAO层返回了一个Optional类,如果我们只想获得其中的某一个属性应该怎么写。

我们借助一个小菜鸡老弟的代码说,这段代码写的只能说一言难尽,按照他的想法,他只想拿到age字段,其实我们只需要借助map即可满足需求。

aa.map(Studnet::getAge)
.orElseThrow(
   () -> throw new Exception("user not found")
);

这里的orElseThrow其实就是传进去的一个方法,我们可以在方法里做些别的事,比如打印下信息之类的

aa.map(Student::getAge).orElseThrow(() -> {
    System.out.println("error! error!");
    return new Exception("user not found");
});

这就是一个很简单的函数式编程例子,把函数作为参数,在另一个函数中调用。这里举一个真实的业务案例,根据高内聚低耦合的抽象思想,我们保存数据的时候做一层前置数据校验,不同的类有不同的检查字段和方法,比如Student类要检查年龄是否大于18岁。

public class Java8Test {

    public static void main(String[] args) {
        Student student1 = Student.builder().age(10).name("uptown").build();
        savePeople(student1, (Student s1) -> {
            if (s1.getAge() < 18) {
                throw new RuntimeException("18岁禁!");
            }
        });
    }

    public static <T extends People> void savePeople(T t, Consumer<T> function) {
        function.accept(t);
        // 业务TODO
    }
}

@Data
@Builder
class People {

}

@Data
@Builder
class Student extends People {
    Integer age;
    String name;
}

这样写是不是比叠加if语句优化很多。

闭包

熟练使用函数式编程的jym对这个概念肯定不陌生,闭包就是能够读取其他函数内部变量的函数,借助网上js闭包的例子。

function makeFunc() {
	var name = "Mozilla";
	function displayName() {
		console.log(name);
	}
	return displayName;
}

var myFunc = makeFunc();
myFunc(); // "Mozilla"

可以通俗的想象成,你在房间里写了一段代码包含一个函数和一些变量。当你离开房间后,函数和变量仍然存在,它们就形成了一个闭包。当你再次进入房间时,你可以使用闭包中的函数来访问和修改之前已经修改过的变量,变量仍然记录着之前的状态。

那么Java中是否有闭包特性呢,答案是严格意义上说没有。

因为仔细想一下就发现在JVM里这套逻辑显然不符合,在栈中的变量函数结束后就会被清掉,怎么会记住之前的结果。但我们可以通过匿名内部类或lambad表达式实现,lambad本质上也是匿名内部类。

public static void main(String[] args) {
    final int i = 0;
    Supplier<Integer> sup = new Supplier<>(){
        Integer get(){
            return i;
        }
    };
}

为了保证正确性和一致性,JDK在语法上规定了lambad内部访问的局部变量必须是final修饰的。因为lambda表达式本质上创建了一个闭包,捕获了外部的局部变量,并在Lambda表达式的函数体中使用这些变量。

这只是其中一点,变量修饰为final还有一些数据一致方面的问题,这里就不再赘述了。

以上就是Java的函数式编程详解的详细内容,更多关于Java函数式编程的资料请关注好代码网其它相关文章!

标签: Java 函数式 编程