开篇
JDK8大家知道并使用的特性莫过于Lambda,这个很多人会用,比如Stream流,但都是简单浅显的使用,比如调用集合的Stream API等,但不会自己定义函数接口或API,今天通过几个案例来提高下对Java 中函数式编程的使用。
案例演示
函数接口说明
接口 | 输入参数 | 返回类型 | 说明 |
UnaryOperator | T | T | 一元函数,输入输出类型相同 |
Predicate | T | boolean | 断言 |
Consumer | T | / | 消费一个数据,只有输入没有输出 |
Function<T,R> | T | R | 输入 T 返回 R,有输入也有输出 |
Supplier | / | T | 提供一个数据,没有输入只有输出 |
BiFunction<T,U,R> | (T,U) | R | 两个输入参数 |
BiPredicate<L, R> | (L,R) | boolean | 两个输入参数 |
BiConsumer<T, U> | (T,U) | void | 两个输入参数 |
BinaryOperator | (T,T) | T | 二元函数,输入输出类型相同 |
案例一
Function<Integer, Integer> times2 = e -> e * 2; Function<Integer, Integer> squared = e -> e * e; // 先执行参数,再执行调用者 /* * 1. 4 * 4 = 16 16 * 2 = 32 */ System.out.println("result: " + times2.compose(squared).apply(4)); // 32 /* * 先执行调用者: 4 * 2 = 8 再执行then传入的function 8 * 8 = 64 */ System.out.println("result: " + times2.andThen(squared).apply(4)); // 64 复制代码
这两个方法,andThen
表示在外面那个Function执行之后调用,compose
表示在外面那个Function执行之前调用。
Tests passed: 1 of 1 test - 46 ms result:32 result:64
案例二
@Test public void test2(){ // 3个数相加 Function<Integer, Function<Integer, IntFunction<Integer>>> addfun3 = x -> y -> z -> x + y + z ; // 7个数相加 Function<Integer, Function<Integer, Function<Integer, Function<Integer, Function<Integer, Function<Integer, IntFunction<Integer>>>>>>> addfun7 = x -> y -> z -> a -> c -> b -> d -> x + y + z + a + c + b + d; // 1+2+3 Integer sum = addfun3.apply(1).apply(2).apply(3); System.out.println(sum); } 复制代码
通过这种方式,可以实现链式编程的累加效果
Tests passed: 1 of 1 test- 89r
案例三
@SafeVarargs private static <R> Function<R, R> combineFunctions(Function<R, R>... functions) { return Arrays.stream(functions) .reduce(Function::andThen) .orElseThrow(() -> new IllegalArgumentException("No functions to combine")); } @Test public void test3() { Function<Integer, Integer> addfun2 = x -> x * x; final Integer apply = combineFunctions(addfun2, addfun2).apply(2); System.out.println(apply); String str = "1,2,3,4,5,6"; Function<Object, Object> splitByComma = s -> ((String) s).split(","); Function<Object, Object> convertToInt = tokens -> Stream.of((String[]) tokens).map(Integer::valueOf).toArray(Integer[]::new); Function<Object, Object> findMax = ints -> Stream.of((Integer[]) ints).max(Integer::compare).get(); Integer max = (Integer) combineFunctions(splitByComma, convertToInt, findMax).apply(str); System.out.println(max); } 复制代码
如果你想有一个接受可变长度的Function参数的方法,可以使用...
描述符,其中泛型<R,R>
代表输入与输入类型,使用泛型是为了兼容性更高
Tests passed: 1 of 1 test - 59 ms 16
案例四
@Test public void test4(){ Function<Integer, Integer> addfun2 = x -> x * x; final Calculator<Integer, Integer> calculator = new Calculator<>(2); final Integer integer = calculator.combineFunctions(addfun2, addfun2); System.out.println(integer); } public class Calculator<R,T> { // 被操作的属性 private Object input; public Calculator(Object input) { this.input = input; } // 可以通过这种方式定义对象自身的行为 @SuppressWarnings("unchecked") @SafeVarargs public final R combineFunctions(Function<T, T>... functions) { return (R) Arrays.stream(functions) .reduce(Function::andThen) .orElseThrow(() -> new IllegalArgumentException("No functions to combine")) .apply((T)input); } }
领域开发模式或多或少都听过、了解过,对象除了有自身的属性之外还可以有自身的行为,针对对象的行为方法也是可以使用函数式编程范式来定义的。
Tests passed: 1 of 1 test - 47 m 16
案例五
// BiConsumer<T, Integer> 两个输入参数T、Integer public static <T> Consumer<T> consumerWithIndex(BiConsumer<T, Integer> consumer) { class Obj { int i; } // 只会被调用一次,原因看java.lang.Iterable#forEach Obj obj = new Obj(); // 返回的Consumer函数 return t -> { int index = obj.i++; // 这里执行System.out.println("list[" + index + "]=" + item),消费指定泛型的数据。 consumer.accept(t, index); }; } @Test public void test5(){ val list = Arrays.asList("Hi", "I", "am", "Henry.Yao"); // 2个元素为一组 val partition = Lists.partition(list, 2); partition.forEach(LambdaUtils.consumerWithIndex((item, index) -> { System.out.println("list[" + index + "]=" + item); })); } 复制代码
Java8的forEach()
循环对象的时候,是没有办法得到对象索引下标的,针对这个情况,可以声明一个函数式方法来做到,最终书写形式类似Scala、js的forEach语法,很有用!
Tests passed: 1 of 1 test -88 ms list[0]=[Hi,I] list[1]=[am, Henry.Yao]
java.lang.Iterable#forEach
// action:是一个Consumer函数 default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { /* 这里会循环调用Consumer函数,而consumerWithIndex返回的Consumer函数内容为 t -> { int index = obj.i++; consumer.accept(t, index); } 所以Obj obj = new Obj()只会被调用一次,这样就不用担心new Obj 时把 i 没有重置为0,不会发生的 */ consumer.accept(t, index); } action.accept(t); } }