JDK8stream的"烧"操作

背景

JDK8 可以说是JDK的一个分水岭,JDK8中加入了很多新特性,JDK8的编码风格也和之前有一些变化,之前更注重代码的整洁,在性能和可阅读性方面做了一些平衡,而JDK8的风格更激进,代码紧凑,引入了一些很特别的写法,可阅读性有所下降。

其中比较引人注目的有:

1、Lambda表达式

2、集合操作

3、Map的改进

第三点属于思路和性能方面的提升,值得学习。

第一点和第二点 属于语言开创性的东西,它们为我们提供了一些新的方式来编写代码,给开发者了更多的空间来做一些操作。

这些特性提供了很多非常便利的操作,在(JDK8)之前我们需要大段代码才能实现的东西,现在已经实现起来非常简洁,代码清爽。

比如:

list.forEach(System.out::println);
还有
.stream()//转成Stream
.filter(team -> team!=null)//过滤
.distinct()//去重
.mapToInt(num->num*2)//map操作
.skip(2)//跳过前2个元素
.limit(4)//限制取前4个元素
.peek(System.out::println)//流式处理对象函数
.sum());// 求和这些操作在很多场景都能应用,非常方便和简介。
====================================== 这有一条分割线==================================================================
但是这种操作的缺点(应该说特性/特点)很容易让人误用,我经常能在同事的代码中看见同事写一大段这样的操作。
比如:

Map<Integer, List<AbstractMap.SimpleEntry<Integer, Object>>> collect = lsit.stream()
  .flatMap(e -> IntStream.range(e.getLowerLevel(), e.getUpperLevel())
  .mapToObj(i -> new AbstractMap.SimpleEntry<>(i, e)))
  .collect(Collectors.groupingBy(p -> p.getKey()));

//转换为Map<Integer, List<Object>>, key = level, value 为该level对应的一组Object
return collect.entrySet().stream()
  .map(e -> new AbstractMap.SimpleEntry<>(e.getKey(), e.getValue().stream()
  .map(o -> o.getValue())
  .collect(Collectors.toList())))
  .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));

流(链)式操作的误操作就是可以一直"."下去。

这种代码的问题根源就是将自身的一堆业务逻辑和java流式特性混在一起;

这段代码逻辑对于作者来说可能很清晰,他也能很好的管理这段代码,但是对于其他人呢,很可能他明天就会离职了;

也许很多人会说,你看JDK中很多这种实现,或者那个知名的框架也有很多这样的写法,我们为什么不这样做呢?

但是我们真的需要这样做吗?

不,我们的代码不是JDK,也不是知名的框架,我们的代码更多关注的是业务逻辑,这也是我们使用JDK和很多框架的原因;

我们不是来show代码简洁程度,我们需要关注的是逻辑是否正确,是否清晰,是否易维护;

我们的产品会随着业务发展而变化,我们的代码可能会经常改动,逻辑清晰和易维护应该是需要重点考虑;

更重要的是,我们不是大神,我们只是模仿者,我们是写不出毫无破绽的代码,我们的代码有多如牛毛的BUG,这种代码如何debug,如何动手术?

====================================== 这有一条分割线==================================================================

最近阅读代码发现有同时这样写:

  List lotteryActivities = ;

  service.query(lotteryActivities.stream().map(LotteryActivity::getCode).collect(Collectors.toList())).forEach(t ->
    redisTemplate.delete(RedisKeyGenerator.getStockNoticeStatusKey(t.getLotteryCode(), t.getId()))
  );

上面是一条语句,但是确包含了3个独立的操作:

  1、将一个(LotteryActivity)list转换成一个(LotteryActivity::getCode)list

  2、使用lotteryItemService,根据code集合查询出一个集合

  3、遍历第2步的集合,根据集合中对象的lotteryCode, id 删除redis数据

将一些复杂的操作揉成一条很长语句,我想这应该不是流失编程设计的初衷,这样的写法会导致代码更难阅读和维护;

特别是在方法调用时,使用一个很长的表达式来设置参数,降低了代码阅读性;

结束语

1、不是JDK8新特性有问题,这些特性很好,也很易用/常用

2、我们应该更关注业务,关注变化/维护,而不是简洁

3、任何需要额外注释的语句,应该考虑抽象成独立的模块或方法,哪怕它只有一句代码

posted @ 2018-11-13 20:33  lion_eagle  阅读(501)  评论(0编辑  收藏  举报