Java BiFunction 接口实例

1. 简介
JAVA8 引入了函数式编程 , 可以把函数作为参数传入实现通用方法 。熟知的 Java 8 单个参数函数式接口比如 Function、Predicate 和 Consumer 。
这篇教程会介绍如何使用支持两个参数的函数式接口 。这样的函数被称为二元函数 , 在 Java 中使用 BiFunction 函数式接口 。
2. 单个参数函数
让我们快速回顾一下如何使用单个参数函数或者一元函数 , 就像在Stream教程中实现的示例:
List<String> mApped = Stream.of("hello", "world") .map(word -> word + "!") .collect(Collectors.toList());assertThat(mapped).containsExactly("hello!", "world!"); 
上面的单元测试中 , map 方法接受1个 Function 类型作为参数 , 对给定的的输入操作后返回结果 。
3. 双参操作
Java Stream 库提供了 reduce 函数 , 可以组合 Stream 中的元素 。在这里定义已接收的数据如何与下一个操作转换 。
reduce 接受 BinaryOperator<T> 类型作为参数 , 支持输入两个类型相同的对象 。
假设要求把 Stream 中的数据通过破折号连接起来 , 下面是几种实现方案 。
3.1. 使用 Lambda 表达式
用 Lambda 实现 BiFunction , 前面是由括号包围的两个参数:
String result = Stream.of("hello", "world") .reduce("", (a, b) -> b + "-" + a);assertThat(result).isEqualTo("world-hello-"); 
上面的示例中 , a、b 两个值是字符串类型 。Lambda 实现把两个参数按照要求组合 , b 在前 , a 在后 , 中间是破折号 。
可以看到 reduce 的第一个参数是空字符串 , Stream 中的第一个值会与空字符串连接 。
另外可以注意到 , Java 类型推断在大多数情况下可以忽略参数类型 。在 Lambda 上下文类型不明确的情况下 , 可以为参数加上类型:
String result = Stream.of("hello", "world") .reduce("", (String a, String b) -> b + "-" + a); 
3.2. 使用 Function
如果上面的算法要求不在结尾加上破折号该怎么处理?可以在 lambda 中编写更多代码 , 但这可能会让代码变得更杂乱 。可以把它抽取成一个函数:
private String combineWithoutTrailingDash(String a, String b) { if (a.isEmpty()) { return b; } return b + "-" + a;} 
然后调用:
String result = Stream.of("hello", "world") .reduce("", (a, b) -> combineWithoutTrailingDash(a, b));assertThat(result).isEqualTo("world-hello"); 
像上面这样 , lambda 会调用抽取出来的函数 。不仅更易于阅读 , 而且可以扩充到更复杂的实现 。
3.3. 使用方法引用
一些 IDE 会自动提示 , 把面的 lambda 转换为方法引用 , 这样读起来会更清晰 。
重写上面的代码 , 改为方法引用:
String result = Stream.of("hello", "world") .reduce("", this::combineWithoutTrailingDash);assertThat(result).isEqualTo("world-hello"); 
方法引用通常让函数式代码自解释性变得更强 。
4. 使用 BiFunction
到目前为止 , 我们介绍了如何对两个相同类型的参数使用函数 。BiFunction 接口支持不同参数的类型 且返回值可以是第三种类型 。
假设要求把两个长度相等的列表合并成一个结果列表 , 对每对输入值执行操作计算结果:
List<String> list1 = Arrays.asList("a", "b", "c");List<Integer> list2 = Arrays.asList(1, 2, 3);List<String> result = new ArrayList<>();for (int i=0; i < list1.size(); i++) { result.add(list1.get(i) + list2.get(i));}assertThat(result).containsExactly("a1", "b2", "c3"); 
4.1. 为 Function 增加范型
可以使用 BiFunction 为方法增加范型 combiner:
private static <T, U, R> List<R> listCombiner( List<T> list1, List<U> list2, BiFunction<T, U, R> combiner) { List<R> result = new ArrayList<>(); for (int i = 0; i < list1.size(); i++) { result.add(combiner.apply(list1.get(i), list2.get(i))); } return result;} 
上面的代码包含了三种参数类型:第一个列表中的元素类型为 T , 第二个列表中的元素类型为 U , 组合函数调用后的元素类型为 R 。
调用 BiFunction 的 apply 方法 得到结果 。
4.2. 调用范型函数


推荐阅读