随着 JDK 1.8 的推出,Java 也开始拥有的一些函数式编程了能力了。我们今天从一种语义化的函数 flatMap
去摸索使用 Java 进行函数式编程。我们先展示一段代码,代码演示的是一段登录场景的逻辑实现:
// loginCredentialDao 去查询匹配的登录键(没找到返回空)
return Optional.ofNullable(loginCredentialDao.find(LoginCredentialType.USERNAME, request.credential))
// 找到了 loginCredential, userDao 再根据 userId 去查询对应的用户信息(没找到返回空)
.flatMap(loginCredentialPO -> Optional.ofNullable(userDao.findById(loginCredentialPO.getUserId())))
// 找到以后如果用户设置了密码且密码相等
.filter(userPO -> nonNull(userPO.getPassword()) && userPO.getPassword().equals(request.password))
// 则返回登录成功结果
.map(userPO -> new LoginResponse(1, userPO.getName(), ""))
// 任意一个信息未找到,返回失败结果
.orElseGet(() -> new LoginResponse(0, "", "账号或密码错误"));
这里还有两个比较容易理解的函数 filter 和 map,filter 是容器中(专业的说法也可以是 monad)过滤数据,map 则是将容器中的数据映射成另外一个数据。而 flatMap
的则是扁平化映射数据(和 map 的效果很像),这是一种专业说法,每个字都认识,但是却不懂是什么意思。我们从上面的例子中来看看它产生了什么效果。
Optional.ofNullable
自身是一个 Optional 类型,我们处理它包裹的值,但是处理的结果仍然会返回一个 Optional 类型,如果是普通的 map 函数处理,我们容器中的值会变成容器包裹着一个容器,类似这样的类型 Optional<Optional<>>,下面的操作碰到这样一个参数那一定是想骂人了(就像平时碰到的List<List<>> 类型)。实际上我们想要的效果是什么样呢?只要这个 Optional 任意一层没有,它都就是没有,也就是只要里面这层 Optional 是 empty,那外面那层也应该是 empty。这时候就是 flatMap
起的作用啦,如果你处理函数返回的依然是一个容器包裹的对象,那么它会合并两层容器为一层。从上面的例子上外面能看到,后面的处理函数就不用先 get()
值再处理了。
flatMap
是数值处理的利器,而数值处理又是函数式编程最主要的内容,所以它还是一个值得好好理解的函数的。