Java函数式编程详解


JDK 8Java 引入全新功能特性,如 streamtime 包等等,但今天的介绍的重点是函数编程。

一、Function

1. 基本介绍

Function 是在 JDK 8 中新添加的特性,即通过匿名函数方式将传统的方式转化为一个对象,如此设计最大的优点就是可以将一个方法封装为参数传入另一个方法,在一些开发场景中提高代码的复用率,在后面将会详细的进行介绍说明。

2. 初始化

Function<T, R> 在初始化时需要制定两个类型,其中 T 为函数入参类型, R 为函数返回类型。

定义之后的 Function 通过 apply() 方法调用,传入的数据类型由初始化确定。

@Test
public void demo() {
    Function<Integer, String> function = (it) -> {
        int sum = 0;
        for (int i = 0; i < it; i++) {
            sum += i;
        }
        return "Function total: " + sum;
    };
    // 通过 apply() 调用
    String result1 = function.apply(5);
    System.out.println(result1);
}

如果上述的代码结构你仍然有点不混淆,下面就将上述代码转化为常用的方法格式。

@Test
public void demo() {
    // 普通方法调用
    String result1 = calculate(5);
    System.out.println(result1);
}

public String calculate(int num) {
    int sum = 0;
    for (int i = 0; i < it; i++) {
        sum += i;
    }
    return "Function total: " + sum;
}

3. 多参数

通过上述的例子相信你已经了解了 Function 函数的基本使用了,但从刚才的例子你或许也发现了一个问题,那就是其默认只能传一个参数,如果需要传入多个参数呢?

为此 JDK 中提供了 Function<T[], R> 用于传入一组同类型参数,但此方式将大大降低代码的可读性,不建议在实际开发中使用。

下面就介绍另外两种多参数初始化方式。

(1) BiFunctiuon

BiFunctiuon<T1, T2, R>Function 的基础上进行了扩展,允许传入两个参数,其中 T1 是第一个参数类型,T2 为第二个参数类型, R 仍然为计算的结果类型。

其同样是通过 apply() 方法调用,下面通过一个示例演示效果:

public void demo1() {
    BiFunction<Integer, Integer, String> biFunction = (val1, val2) -> {
        int sum = 0;
        for (int i = val1; i < val2; i++) {
            sum += i;
        }
        return "BiFunction total: " + sum;
    };
    String result = biFunction.apply(5, 10);
    System.out.println(result);
}
(2) TriFunction

TriFunction<T1, T2, T3, R> 故名思义,即允许传入三个参数,基本上能够覆盖开发中的大部分场景,这里就不重复介绍了。

二、Consumer

1. 基本介绍

在上面中我们初步介绍了 Function 的作用,可以在之前的例子中看出其都是有返回值的,而 Consumer 基本作用与 Function 类型,但最大的不同之处在于其没有返回值,下面直接通过例子进行说明。

2. 初始化

Consumer<T> 因为没有返回值,所以初始化时参数只有一个,没有返回值类型。

Consumer 中调用函数是通过 accept() 方法,这点与 Function 有所区别。

@Test
public void demo() {
    Consumer<Integer> consumer = (it) -> {
        int sum = 0;
        for (int i = 0; i < it; i++) {
            sum += i;
        }
        System.out.println("Consumer total: " + sum);
    };
    // 通过 accept() 调用
    consumer.accept(5);
}

3. 多参数

ConsumerFunction 类似,同样提供 BiConsumer 用于传入两个参数,但注意其并没有提供两个参数以上的初始化方式。

具体的 BiConsumer 初始化声明方式与 BiFunction 类似,这里不进行过多的阐述。

4. 示例演示

下面通过一个具体的示例演示将 Consumer 作为参数传入方法执行。

在下述示例中定义了泛型方法 funProcess() 用于拆分任务分批执行,通过 batchSize 指定单批次的大小,dataList 为需要拆分执行的目标集合, consumer 即为单次执行的具体内容实现。

@Test
public void demo() {
    Consumer<Map<String, Object>> consumer1 = (it) -> {
        List<String> data = (List<String>) it.get("data");
        System.out.println("Consumer1 active: " + data);
    };
    <Map<String, Object>> consumer2 = (it) -> {
        String value1 = (String) it.get("key-1");
        List<String> data = (List<String>) it.get("data");
        System.out.println("Consumer-1 active: " +value1 + ", data= " + data);
    };

    List<String> list = new ArrayList<>();
    for (int i = 0; i < 8; i++) {
        list.add(String.valueOf(i));
    }
    Map<String, Object> params = new HashMap<>();
    params.put("key-1", "value-1");
    System.out.println("List: " + list);
    funProcess(2, list, new HashMap<>(), consumer1);
    funProcess(2, list, params, consumer2);
}

public <T> void funProcess(int batchSize, List<T> dataList,
                            Map<String, Object> params, Consumer<Map<String, Object>> consumer) {
    if (Objects.isNull(dataList) || dataList.isEmpty()) {
        return;
    }

    int start = 0;
    int size = dataList.size();
    int end = Math.min(size, batchSize);
    List<T> tempList;
    while (start <= end) {
        if (start == end) {
            break;
        }
        try {
            tempList = dataList.subList(start, end);
            params.put("data", tempList);
            consumer.accept(params);
        } catch (Exception e) {
            e.printStackTrace();
        }
        start = end;
        end = size - end < batchSize ? size : end + batchSize;
    }
}

三、Predicate

Predicate<T>Function 的一类特例,允许接收单个参数并最终返回 boolean 类型,与 Function<T, Boolean> 作用效果等价。

通过 test() 方法触发 Predicate 函数,下面看一个具体示例。

public void demo1() {
    Predicate<Integer> predicate = (it) -> {
        int num = 0;
        num += it;
        return num > 0;
    };
    boolean result = predicate.test(1);
    // true
    System.out.println(result);
}

四、Supplier

Supplier<R> 函数接口不允许传入参数,最终返回 R 类型结果,通过 get() 方法触发函数。

下面看一个 Supplier 的基本示例:

public void demo2() {
    Supplier<String> supplier = () -> {
        return "Hello world!";
    };
    String result = supplier.get();
    // Hello world!
    System.out.println(result);
}

文章作者: 烽火戏诸诸诸侯
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 烽火戏诸诸诸侯 !
  目录