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、任何需要额外注释的语句,应该考虑抽象成独立的模块或方法,哪怕它只有一句代码