摘要: 通过一次并发处理数据集的 Java 代码 重构 之旅, 展示函数式编程如何使得代码更加精练
难度: 中级
基础知识
在开始之前, 了解高阶函数和泛型这两个概念是必要的
高阶函数就是接收函数参数的函数, 能够根据传入的函数参数调节自己的行为类似 C 语言中接收函数指针的函数最经典的就是接收排序比较函数的排序函数高阶函数不神秘哦! 在 Java8 之前, 就是那些可以接收回调接口作为参数的方法; 在本文中, 那么接收 Function, Consumer, Supplier 作为参数的函数都是高阶函数高阶函数使得函数的能力更加灵活多变
泛型是能够接纳多种类型作为参数进行处理的能力很多函数的功能并不限于某一种具体的类型, 比如快速排序, 不仅可以用于整型, 也可以用于字符串, 甚至可用于对象泛型使得函数在类型处理上更加灵活
高阶函数和泛型两个特点结合起来, 可使得函数具备强大的抽象表达能力
重构前
基本代码如下主要用途是根据具体的业务数据获取接口 IGetBizData , 并发地获取指定 Keys 值对应的业务数据集
- package zzz.study.
- function.refactor.before;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.concurrent.Callable;
- import java.util.concurrent.CompletionService;
- import java.util.concurrent.ExecutorCompletionService;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import zzz.study.
- function.refactor.TaskUtil;
- /**
- * Created by shuqin on 17/6/23.
- */
- public class ConcurrentDataHandlerFrame {
- public static void main(String[] args) {
- List < Integer > allData = getAllData(getKeys(), new GetTradeData());
- System.out.println(allData);
- }
- public static List < String > getKeys() {
- List < String > keys = new ArrayList < String > ();
- for (int i = 0; i < 20000; i++) {
- keys.add(String.valueOf(i));
- }
- return keys;
- }
- /**
- * 获取所有业务数据
- */
- public static < T > List < T > getAllData(List < String > allKeys, final IGetBizData iGetBizData) {
- List < String > parts = TaskUtil.divide(allKeys.size(), 1000);
- System.out.println(parts);
- ExecutorService executor = Executors.newFixedThreadPool(parts.size());
- CompletionService < List < T >> completionService = new ExecutorCompletionService < List < T >> (executor);
- for (String part: parts) {
- int start = Integer.parseInt(part.split(":")[0]);
- int end = Integer.parseInt(part.split(":")[1]);
- if (end > allKeys.size()) {
- end = allKeys.size();
- }
- final List < String > tmpRowkeyList = allKeys.subList(start, end);
- completionService.submit(new Callable < List < T >> () {
- public List < T > call() throws Exception {
- return iGetBizData.getData(tmpRowkeyList);
- }
- });
- }
- List < T > result = new ArrayList < T > ();
- for (int i = 0; i < parts.size(); i++) {
- try {
- result.addAll(completionService.take().get());
- } catch(Exception e) {
- e.printStackTrace();
- }
- }
- executor.shutdown();
- return result;
- }
- }
- /** 业务数据接口 */
- interface IGetBizData < T > {
- List < T > getData(List < String > keys);
- }
- /** 获取业务数据具体实现 */
- class GetTradeData implements IGetBizData < Integer > {
- public List < Integer > getData(List < String > keys) {
- // maybe xxxService.getData(keys);
- List < Integer > result = new ArrayList < Integer > ();
- for (String key: keys) {
- result.add(Integer.valueOf(key) % 1000000000);
- }
- return result;
- }
- }
代码本身写得不坏, 没有拗口的地方, 读起来也比较流畅美中不足的是, 不够通用化 心急的读者可以看看最后面重构后的代码这里还是从重构过程开始
重构过程
从小处着手
如果面对一大块代码不知如何下手, 那么就从小处着手, 先动起来 对于如下代码, 了解 Java8 Stream api 的同学肯定知道怎么做了:
- public List<Integer> getData(List<String> keys) {
- // maybe xxxService.getData(keys);
- List<Integer> result = new ArrayList<Integer>();
- for (String key: keys) {
- result.add(Integer.valueOf(key) % 1000000000);
- }
- return result;
- }
可以写成一行代码:
return keys.stream().map(key -> Integer.valueOf(key) % 1000000000).collect(Collectors.toList());
不过, 写多了, collect(Collectors.toList()) 会大量出现, 占篇幅, 而且当 map 里的函数比较复杂时, IDE 有时不能自动补全注意到这个函数其实就是传一个列表和一个数据处理函数, 因此, 可以抽离出一个 StreamUtil , 之前的代码可以写成:
- public static <T,R> List<R> map(List<T> data, Function<T, R> mapFunc) {
- return data.stream().map(mapFunc).collect(Collectors.toList()); // stream replace foreach
- }
- return StreamUtil.map(keys, key -> Integer.valueOf(key) % 1000000000);
看上去是一个很平常的改动, 实际上是一大步注意到 map(keys, key -> Integer.valueOf(key) % 1000000000) 并没有展示该如何去计算, 只是表达了要做什么计算 从关注计算过程 到描述计算内容, 实现了计算描述 与执行的关注点分离
好滴, 已经走出了第一步!
重复的 foreach 代码
自从了解了函数编程, 似乎对重复的 foreach 代码生出仇了, 恨不得消灭干净 读者可以看到方法 getKeys 和 getAllData (从 completionService 获取数据时) 分别有一段 foreach 循环, 通过计数然后添加元素并返回一个列表 (具体就不贴代码了) 这样的代码看多了也会厌倦的 实际上, 可以抽离出一个 ForeachUtil 的公用类来做这个事情为避免代码占篇幅, 读者可以看重构后的 ForeachUtil, 然后 getKeys 的实现就可以凝练为一行代码:
- getKeys:
- return ForeachUtil.foreachAddWithReturn(2000, (ind -> Arrays.asList(String.valueOf(ind))));
- getAllData:
- List<R> result = ForeachUtil.foreachAddWithReturn(parts.size(), (ind) -> get(ind, completionService));
棒! 每次将多行代码变成一行代码是不是很爽? 更重要的是, 每次都抽离出了通用的部分, 可以让后面的代码更好写
注意到, 由于 lambda 表达式无法处理受检异常, 因此, 将 get 函数抽离出来成为一个函数, lambda 表达式就显得更好看一点
lambda 取代内部类
注意到 getAllData 里有一个比较难看的内部类, 是为了根据一段逻辑生成一个任务类:
- completionService.submit(new Callable<List<T>>() {
- public List<T> call() throws Exception {
- return iGetBizData.getData(tmpRowkeyList);
- }
- });
实际上, 优秀的 IDE 工具比如 Intellj 会自动提示要不要替换成 lambda 就依它的建议:
completionService.submit(() -> iGetBizData.getData(tmpRowkeyList));
又是一行代码! 干净利落!
简单而有益的隔离
这里有一段代码, 根据任务划分的区段范围, 获取数据集的指定子集:
- for (String part: parts) {
- int start = Integer.parseInt(part.split(":")[0]);
- int end = Integer.parseInt(part.split(":")[1]);
- if (end > allKeys.size()) {
- end = allKeys.size();
- }
- final List<String> tmpRowkeyList = allKeys.subList(start, end);
- // submit tasks
- }
本来是一段容易编写单测的独立逻辑块, 混在 getAllData 方法里, 一来让这段代码的单测难写了, 二来增加了整个方法 getAllData 的单测编写麻烦度真是两不讨好抽离出去更好可参见重构后的 TaskUtil. 很多程序猿都有这个容易导致单测难写的不良习惯
回调接口改造成函数接口
接下来做什么呢? 看上去小的改动似乎到尽头了 现在, 可以考虑改造回调接口了实际上, 函数接口是回调接口的非常有效的替代者可以把 getAllData 的参数 final IGetBizData iGetBizData 改成 Function<List<String>, List<T>> iGetBizDataFunc, 表示这个函数将作用于一个列表 keys, 返回指定的数据集相应的, iGetBizData.getData(tmpRowkeyList) 就可以改成 iGetBizDataFunc.apply(tmpRowkeyList) 就是这么简单!
读者可能会疑惑, 这样改究竟有什么益处呢? 第一个好处就是可以移除 iGetBizData 接口定义了 java8 之前, 每次写回调, 都得定义一个接口, 再写实现类, 烦不烦?
新的需求
假设现在我不仅需要并发获取数据, 还需要并发处理数据得到一个数据列表, 该怎么办呢? 看上去 getAllData 已经有潜力满足需求了, 可是还有一些细节要处理实际上, 无非就是给定一个 T 类型列表, 以及一个处理列表并返回另一个 R 类型列表的函数, 然后利用 getAllData 已有的功能就可以实现 可以抽离出一个底层的 public static <T,R> List<R> handleAllData(List<T> allKeys, Function<List<T>, List<R>> handleBizDataFunc) 方法, 然后将 getAllData 的实现移入其中, 对类型略加改造, 就可以实现然后 getAllData 就可以依赖 handleAllData 来实现了泛型很强大!
抽离异常处理
我们常常会在代码里看到很多 try-catch 语句块大多数程序猿可能并不觉得有什么, 可是, 重复就是代码罪恶之源实际上, 消除这些重复有一个简单的技巧: 首先看这些重复函数里有哪几行语句是不一样的(通常是一行或两三行), 抽离出 Function (单参数单返回函数) 或 Consumer (单参数无返回函数) 或 BiFunction (双参数单返回函数) 或 BiConsumer (双参数无返回函数) , 然后将这个函数接口作为参数传进去 function 的方法是 apply, consumer 的方法是 accept ;
重构后的代码可见 CatchUtil 实际上很像 Python 里的装饰器, 通过封装函数的 try-catch , 给任何函数添加异常处理 不过 Python 有万能函数 func(*args, **keyargs) , Java 没有可以表示所有函数接口的万能函数可参见文章: python 使用装饰器捕获异常
抽离并发处理
接下来, 我们需要抽离出并发处理客户端代码不需要知道数据处理的细节, 它只需要传一个数据列表和一个数据处理函数, 其他都交给框架层略加修改后, 可参见重构后的代码 ExecutorUtil. 原来一团代码经过精练后, 长度减少了很多 handleAllData 现在变成了这样:
- public static <T,R> List<R> handleAllData(List<T> allKeys, Function<List<T>, List<R>> handleBizDataFunc) {
- return ExecutorUtil.exec(allKeys, handleBizDataFunc);
- }
抽离并发处理的益处在于, 可以在后续使用策略模式, 提供串行计算策略和并发计算策略, 在不同场景下选择不同的计算策略重构后的代码没有展示这一点读者可以一试
过程式改函数式
注意到有 System.out.println(allData); 嗯, 怎么感觉有点不顺眼呢? 其实可以编写一个消费函数, 改成函数式, 见如下代码 YY: 这都要改, 过不过分 ? 可是现在要方便地进行其他简单处理, 就更容易了, 不必编写函数, 而是编写和传入不同的 lambda 表达式即可:
- public static <T> void consumer(List<T> data, Consumer<T> consumer) {
- data.forEach( (t) -> CatchUtil.tryDo(t, consumer) );
- }
- consumer(allData, System.out::println);
- consumer(allData, (s) -> System.out.println(s*3));
更函数式的风格
注意到 handleAllData 需要传一个数据列表 allKeys ; 更函数式的风格, 这个列表应该是一个数据提供函数来获得的可以使用 Supplier 来抽象它有一个 get 函数 可以将 参数改成 Supplier getAllKeysFunc, 然后用 getAllKeysFunc.get() 来取代之前的列表 allKeys.
- public static <T,R> List<R> handleAllData(Supplier<List<T>> getAllKeysFunc, Function<List<T>, List<R>> handleBizDataFunc) {
- return handleAllData(getAllKeysFunc.get(), handleBizDataFunc);
- }
这样有什么益处呢? 抽离了列表 allKeys 的来源, 现在可以从任意地方获取, 比如从文件或网络中获取, 只要传入一个数据提供函数即可, 这使得 handleAllData 的处理范围更加灵活了
模拟柯里化
了解柯里化的同学知道, 柯里化是将多元函数分解为多个单元函数的多次调用的过程, 在每一次分解的过程中, 都会产生大量的副产品函数, 是一个强大的函数工厂柯里化的简单介绍可参见文章: 函数柯里化 (Currying) 示例
如何使用 Java 模拟柯里化呢? 这要求一个并发数据处理函数返回一个函数 Function 而不是一个值列表, 而返回的函数是可定制化的这部分通过尝试及 IDE 的提示, 而完成的见如下代码:
- /**
- * 传入一个数据处理函数, 返回一个可以并发处理数据集的函数, 该函数接受一个指定数据集
- * Java 模拟柯里化: 函数工厂
- */
- public static <T,R> Function<List<T>, List<R>> handleAllData(Function<List<T>, List<R>> handleBizDataFunc) {
- return ts -> handleAllData(ts, handleBizDataFunc);
- }
- /**
- * 传入一个数据提供函数, 返回一个可以并发处理获取的数据集的函数, 该函数接受一个数据处理函数
- * Java 模拟柯里化: 函数工厂
- */
- public static <T,R> Function<Function<List<T>, List<R>>, List<R>> handleAllData(Supplier<List<T>> getAllKeysFunc) {
- return handleBizDataFunc -> handleAllData(getAllKeysFunc.get(), handleBizDataFunc);
- }
然后, 客户端的代码就更加有函数式风格了(甚至显得有点另类) 第一个 handleAllData 接受一个数据处理函数, 并返回一个封装了并发处理的数据处理函数, 可以对任意指定数据集进行处理; 第二个 handleAllData 接受一个数据提供函数, 并返回一个封装了并发处理的数据处理函数, 通过指定定制化的数据处理函数来实现计算 apply 里的对象是一个 Function ! 是不是有点思维反转 ? ^_^ 仔细再体味一下~~
- List<Object> objs = StreamUtil.map(DataSupplier.getKeys(), s->Double.valueOf(s));
- List<Double> handledData2 = handleAllData((numbers) -> StreamUtil.map(numbers, (num) -> Math.pow((double)num,2))).apply(objs);
- Function<List<String>, List<Object>> func = (numbers) -> StreamUtil.map(numbers, (num) -> Integer.parseInt(num)*2);
- List<Object> handledData3 = handleAllData(DataSupplier::getKeys).apply(func);
当然, 这里并不是真正的柯里化, 因为参数只有一个 Scala 的柯里化是指 f(x)(y) = x+y ; f(x) = f(x)(1) = x+1 ; f(y) = f(1)(y) = 1+y ; 可以通过 f(x)(y) 将 x 或 y 代入不同的变量得到任意多的函数利用柯里化很容易写成简洁的微框架, 比如一个文件集合处理框架 filesHandler(files)(handler) 与 filesHandler(hanler)(files) 是不一样的这里不再过多讨论
小结
通过使用函数式编程对过程 / 对象混合式代码进行重构, 使得代码更凝练而有表达力了虽然函数式编程尚未广泛推广于大型工程中, 只有一部分程序猿开始尝试使用, 在理解上也需要一定的思维转换, 不过适度使用确实能增强代码的抽象表达力仅仅是高阶函数 + 泛型 + 惰性求值的基本使用, 就能产生强大而凝练的表达效果 函数式编程确有一套自己独特的编程设计理念 推荐阅读 Scala 函数式编程
现代软件开发已经不仅仅是单纯地编写代码实现逻辑, 而是含有很强的设计过程需要仔细提炼概念对象操作, 仔细设计对象之间的交互, 有效地组合一系列关联对象成为高内聚低耦合的模块, 有效地隔离对象关联最小化依赖关系, 如此才能构建出容易理解和扩展更容易演进的长久发展的软件编程即是设计, 从具象到抽象再到具象的过程
重构后
重构后的代码是这样子滴:
- package zzz.study.
- function.refactor.result;
- import java.util.Arrays;
- import java.util.List;
- import java.util.
- function.Consumer;
- import java.util.
- function.Function;
- import java.util.
- function.Supplier;
- import zzz.study.
- function.refactor.CatchUtil;
- import zzz.study.
- function.refactor.ExecutorUtil;
- import zzz.study.
- function.refactor.ForeachUtil;
- import zzz.study.
- function.refactor.StreamUtil;
- /**
- * Created by shuqin on 17/6/23.
- */
- public class ConcurrentDataHandlerFrameRefactored {
- public static void main(String[] args) {
- List < Integer > allData = getAllData(DataSupplier: :getKeys, GetTradeData: :getData);
- consumer(allData, System.out: :println);
- List < Double > handledData = handleAllData(allData, (numbers) - >StreamUtil.map(numbers, (num) - >Math.sqrt(num)));
- consumer(handledData, System.out: :println);
- List < Object > objs = StreamUtil.map(DataSupplier.getKeys(), s - >Double.valueOf(s));
- List < Double > handledData2 = handleAllData((numbers) - >StreamUtil.map(numbers, (num) - >Math.pow((double) num, 2))).apply(objs);
- consumer(handledData2, System.out: :println);
- Function < List < String > ,
- List < Object >> func = (numbers) - >StreamUtil.map(numbers, (num) - >Integer.parseInt(num) * 2);
- List < Object > handledData3 = handleAllData(DataSupplier: :getKeys).apply(func);
- consumer(handledData3, System.out: :println);
- }
- /**
- * 获取所有业务数据
- *
- * 回调的替换
- */
- public static < T > List < T > getAllData(Supplier < List < String >> getAllKeysFunc, Function < List < String > , List < T >> iGetBizDataFunc) {
- return getAllData(getAllKeysFunc.get(), iGetBizDataFunc);
- }
- public static < T > List < T > getAllData(List < String > allKeys, Function < List < String > , List < T >> iGetBizDataFunc) {
- return handleAllData(allKeys, iGetBizDataFunc);
- }
- public static < T,
- R > List < R > handleAllData(Supplier < List < T >> getAllKeysFunc, Function < List < T > , List < R >> handleBizDataFunc) {
- return handleAllData(getAllKeysFunc.get(), handleBizDataFunc);
- }
- /**
- * 传入一个数据处理函数, 返回一个可以并发处理数据集的函数, 该函数接受一个指定数据集
- * Java 模拟柯里化: 函数工厂
- */
- public static < T,
- R > Function < List < T > ,
- List < R >> handleAllData(Function < List < T > , List < R >> handleBizDataFunc) {
- return ts - >handleAllData(ts, handleBizDataFunc);
- }
- /**
- * 传入一个数据提供函数, 返回一个可以并发处理获取的数据集的函数, 该函数接受一个数据处理函数
- * Java 模拟柯里化: 函数工厂
- */
- public static < T,
- R > Function < Function < List < T > ,
- List < R >> ,
- List < R >> handleAllData(Supplier < List < T >> getAllKeysFunc) {
- return handleBizDataFunc - >handleAllData(getAllKeysFunc.get(), handleBizDataFunc);
- }
- public static < T,
- R > List < R > handleAllData(List < T > allKeys, Function < List < T > , List < R >> handleBizDataFunc) {
- return ExecutorUtil.exec(allKeys, handleBizDataFunc);
- }
- public static < T > void consumer(List < T > data, Consumer < T > consumer) {
- data.forEach((t) - >CatchUtil.tryDo(t, consumer));
- }
- public static class DataSupplier {
- public static List < String > getKeys() {
- // foreach code refining
- return ForeachUtil.foreachAddWithReturn(2000, (ind - >Arrays.asList(String.valueOf(ind))));
- }
- }
- /** 获取业务数据具体实现 */
- public static class GetTradeData {
- public static List < Integer > getData(List < String > keys) {
- // maybe xxxService.getData(keys);
- return StreamUtil.map(keys, key - >Integer.valueOf(key) % 1000000000); // stream replace foreach
- }
- }
- }
- ExecutorUtil package zzz.study.
- function.refactor;
- import java.util.List;
- import java.util.concurrent.ArrayBlockingQueue;
- import java.util.concurrent.CompletionService;
- import java.util.concurrent.ExecutorCompletionService;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.ThreadPoolExecutor;
- import java.util.concurrent.TimeUnit;
- import java.util.
- function.Consumer;
- import java.util.
- function.Function;
- /**
- * Created by shuqin on 17/6/25.
- */
- public class ExecutorUtil {
- private ExecutorUtil() {}
- private static final int CORE_CPUS = Runtime.getRuntime().availableProcessors();
- private static final int TASK_SIZE = 1000;
- // a throol pool may be managed by spring
- private static ExecutorService executor = new ThreadPoolExecutor(CORE_CPUS, 10, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue < >(60));
- /**
- * 根据指定的列表关键数据及列表数据处理器, 并发地处理并返回处理后的列表数据集合
- * @param allKeys 列表关键数据
- * @param handleBizDataFunc 列表数据处理器
- * @param <T> 待处理的数据参数类型
- * @param <R> 待返回的数据结果类型
- * @return 处理后的列表数据集合
- *
- * NOTE: 类似实现了 stream.par.map 的功能, 不带延迟计算
- */
- public static < T,
- R > List < R > exec(List < T > allKeys, Function < List < T > , List < R >> handleBizDataFunc) {
- List < String > parts = TaskUtil.divide(allKeys.size(), TASK_SIZE);
- //System.out.println(parts);
- CompletionService < List < R >> completionService = new ExecutorCompletionService < >(executor);
- ForeachUtil.foreachDone(parts, (part) - >{
- final List < T > tmpRowkeyList = TaskUtil.getSubList(allKeys, part);
- completionService.submit(() - >handleBizDataFunc.apply(tmpRowkeyList)); // lambda replace inner class
- });
- // foreach code refining
- List < R > result = ForeachUtil.foreachAddWithReturn(parts.size(), (ind) - >get(ind, completionService));
- return result;
- }
- /**
- * 根据指定的列表关键数据及列表数据处理器, 并发地处理
- * @param allKeys 列表关键数据
- * @param handleBizDataFunc 列表数据处理器
- * @param <T> 待处理的数据参数类型
- *
- * NOTE: foreachDone 的并发版
- */
- public static < T > void exec(List < T > allKeys, Consumer < List < T >> handleBizDataFunc) {
- List < String > parts = TaskUtil.divide(allKeys.size(), TASK_SIZE);
- //System.out.println(parts);
- ForeachUtil.foreachDone(parts, (part) - >{
- final List < T > tmpRowkeyList = TaskUtil.getSubList(allKeys, part);
- executor.execute(() - >handleBizDataFunc.accept(tmpRowkeyList)); // lambda replace inner class
- });
- }
- public static < T > List < T > get(int ind, CompletionService < List < T >> completionService) {
- // lambda cannot handler checked exception
- try {
- return completionService.take().get();
- } catch(Exception e) {
- e.printStackTrace(); // for log
- throw new RuntimeException(e.getCause());
- }
- }
- }
- TaskUtil package zzz.study.
- function.refactor;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * Created by shuqin on 17/1/5.
- */
- public class TaskUtil {
- private TaskUtil() {}
- public static List < String > divide(int totalSize, int persize) {
- List < String > parts = new ArrayList < String > ();
- if (totalSize <= 0 || persize <= 0) {
- return parts;
- }
- if (persize >= totalSize) {
- parts.add("0:" + totalSize);
- return parts;
- }
- int num = totalSize / persize + (totalSize % persize == 0 ? 0 : 1);
- for (int i = 0; i < num; i++) {
- int start = persize * i;
- int end = persize * i + persize;
- if (end > totalSize) {
- end = totalSize;
- }
- parts.add(start + ":" + end);
- }
- return parts;
- }
- public static < T > List < T > getSubList(List < T > allKeys, String part) {
- int start = Integer.parseInt(part.split(":")[0]);
- int end = Integer.parseInt(part.split(":")[1]);
- if (end > allKeys.size()) {
- end = allKeys.size();
- }
- return allKeys.subList(start, end);
- }
- }
- package zzz.study.
- function.refactor;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.
- function.Consumer;
- import java.util.
- function.Function;
- /**
- * Created by shuqin on 17/6/24.
- *
- * foreach 代码通用模板
- */
- public class ForeachUtil {
- public static < T > List < T > foreachAddWithReturn(int num, Function < Integer, List < T >> getFunc) {
- List < T > result = new ArrayList < T > ();
- for (int i = 0; i < num; i++) {
- result.addAll(CatchUtil.tryDo(i, getFunc));
- }
- return result;
- }
- public static < T > void foreachDone(List < T > data, Consumer < T > doFunc) {
- for (T part: data) {
- CatchUtil.tryDo(part, doFunc);
- }
- }
- }
- CatchUtil package zzz.study.
- function.refactor;
- import java.util.
- function.Consumer;
- import java.util.
- function.Function;
- /**
- * Created by shuqin on 17/6/24.
- */
- public class CatchUtil {
- public static < T,
- R > R tryDo(T t, Function < T, R > func) {
- try {
- return func.apply(t);
- } catch(Exception e) {
- e.printStackTrace(); // for log
- throw new RuntimeException(e.getCause());
- }
- }
- public static < T > void tryDo(T t, Consumer < T > func) {
- try {
- func.accept(t);
- } catch(Exception e) {
- e.printStackTrace(); // for log
- throw new RuntimeException(e.getCause());
- }
- }
- }
- StreamUtil package zzz.study.
- function.refactor;
- import java.util.List;
- import java.util.
- function.Function;
- import java.util.stream.Collectors;
- /**
- * Created by shuqin on 17/6/24.
- */
- public class StreamUtil {
- public static < T,
- R > List < R > map(List < T > data, Function < T, R > mapFunc) {
- return data.stream().map(mapFunc).collect(Collectors.toList()); // stream replace foreach
- }
- }
来源: http://www.tuicool.com/articles/vIbA3ua