用函数式写法精简 Java 代码的一个例子

发布于 2020-9-30 16:54
浏览
0收藏

在写代码的时候,经常遇到做一些判断,并在不同条件下执行不同操作。java中还会经常判断一个对象是否为null,并针对是和否两种情况分别进行处理。用函数式写法精简 Java 代码的一个例子-开源基础软件社区

一下用一个小例子说明,同一个逻辑,用函数式和非函数式写出来是什么样子。之后简要地探讨了二者的区别。这个例子是实际中遇到。我们有不同类别的文档索引在elasticsearch中,如果指定了具体的类别,则用该类别对应的indexer返回的查询语句。如果没有指定类别或者有多个类别,则默认在全部类别搜索。

类别和其对应的Indexer保存在indexerMap,categories是用户传进来的类别列表。

// 如果只有一个类目,则从indexerMap中找出该类目对应的indexer并得到searchBuilder。
// 如果不存在对应类目的indexer或者indexer返回来的searchBuilder为空,则用默认的queryAllBuilder

List<String> categories = ...
Map<String, DocIndexer> indexerMap = ...

// 函数式的写法
final SearchSourceBuilder searchSourceBuilder = Option.of(categories)
        .filter(it -> it.size() == 1)
        .map(it -> it.get(0))
        .flatMap(it -> Option.of(indexerMap.get(it)))
        .flatMap(it -> Option.of(it.searchBuilder()))
        .getOrElseLazy(this::queryAllBuilder);

// 非函数式写法
SearchSourceBuilder searchSourceBuilder;
if (categories.size() == 1) {
    DocIndexer indexer = indexerMap.get(categories.get(0));
    if (indexer == null) {
        searchSourceBuilder = this.queryAllBuilder();
    } else {
        SearchSourceBuilder b = indexer.searchBuilder();
        searchSourceBuilder = Objects.requireNonNullElseGet(b, this::queryAllBuilder);
    }
} else {
    searchSourceBuilder = this.queryAllBuilder();
}

函数式的写法,有如下好处:

 

  • 紧凑。整个执行流依次往下,如果执行途中有异常,则算子函数会自动处理。因此,写逻辑的时候,只需要关注happy path即可。即便执行过程中出现了异常,需要用一个替代方案挽救,也就只是多一个算子而已。
  • 临时变量不见了。同样的逻辑,非函数写法需要定义各种临时变量来存储中间结果。多多的临时变量,不够优雅。
  • null不见了。任何没值的情况都可以看做是异常。将异常的处理隐匿于算子之后,让视野所见的代码干净整洁。


函数式和非函数式,是两种不同的思考方式,也是两种不同的语言(如英语和汉语之类)。同样的事务,二者都可以描述,但描述的优雅程度不同。熟悉函数式的思考方式之后,人的思维也会潜移默化地被塑造,思考问题也开始变得函数式起来。

 

算子(map、flatMap以及filter等)把一些常见的逻辑事先实现出来,等到写具体逻辑的时候,只需要简单的拼凑即可完成逻辑的编写了。

 

受函数式语言的启发,我实现了一个java版的函数式lib。其中Option对象有两个子类,Some和None,分别代表有值和无值。java自带的Optional也有类似功能,但当处理过程中有异常时,Optional并不记录这种异常。因此,我在自己的Option版本中,加入了异常信息,让None可以携带Exception从执行流的上游流到下游。其中,也对异步编程做了一点抽象,值得体验一下。

 

本文为 InfoQ 作者【Sean】的原创文章。

标签
已于2020-9-30 16:54:59修改
收藏
回复
举报
回复
添加资源
添加资源将有机会获得更多曝光,你也可以直接关联已上传资源 去关联
    相关推荐