精练代码:一次Java函数式编程的重构之旅

摘要:经过一次并发处理数据集的Java代码重构之旅,展现函数式编程如何使得代码更加精练。html

难度:中级

java

基础知识

在开始以前,了解“高阶函数”和“泛型”这两个概念是必要的。python

高阶函数就是接收函数参数的函数,可以根据传入的函数参数调节本身的行为。相似C语言中接收函数指针的函数。最经典的就是接收排序比较函数的排序函数。高阶函数不神秘哦!在Java8以前,就是那些能够接收回调接口做为参数的方法;在本文中,那么接收 Function, Consumer, Supplier 做为参数的函数都是高阶函数。高阶函数使得函数的能力更加灵活多变。spring

泛型是可以接纳多种类型做为参数进行处理的能力。不少函数的功能并不限于某一种具体的类型,好比快速排序,不只能够用于整型,也能够用于字符串,甚至可用于对象。泛型使得函数在类型处理上更加灵活。编程

高阶函数和泛型两个特色结合起来,可以使得函数具有强大的抽象表达能力。

api

重构前

基本代码以下。主要用途是根据具体的业务数据获取接口 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 的同窗确定知道怎么作了:app

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函数式编程》。

现代软件开发已经不只仅是单纯地编写代码实现逻辑,而是含有很强的设计过程。须要仔细提炼概念、对象、操做,仔细设计对象之间的交互,有效地组合一系列关联对象成为高内聚低耦合的模块,有效地隔离对象关联最小化依赖关系,如此才能构建出容易理解和扩展、更容易演进的长久发展的软件。编程便是设计,从具象到抽象再到具象的过程。

重构后

重构后的代码是这样子滴:

ConcurrentDataHandlerFrameRefactored

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
  }

}
相关文章
相关标签/搜索