使用函数式接口开发,如果针对每个需要使用的场景都自定义一个函数式接口,会导致我们的项目中有很多仅仅当参数的接口。如果我们能自己抽离这些使用场景的共性,则会省去很多自定义函数式接口,相信很多人会遇到这种情况。幸运的是,JDK 已经提前帮我们把这一步做好了。
Java8 之前的函数式接口 Java8 之前是没有函数式接口这个概念的,但是在 Java8 中,有些接口升级成了函数式接口。最常见的当属以下三位选手:
1 . java.lang.Runnable 2 . java.util.concurrent.Callable 3 . java.util.Comparator
Java8 之后的函数式接口 Java8 提供了一系列的函数式接口,主要在 java.util.function
包目录下。在了解这些函数式接口前,再默念几遍之前反复提到的要点:接口名不重要(反正也是匿名),方法名不重要(反正只有一个抽象方法),重要的是能干什么(行为) 。
接口 入参 返回类型 说明 Function<T,R> T R 输入 T 返回 R,有输入有输出 Consumer<T> T / 纯消费,只有输入没有输出 Supplier<T> / T 纯供应,没有输入只有输出 Predicate<T> T boolean 断言,输出布尔类型 UnaryOperator<T> T T 一元函数,输入输出类型相同 BinaryOperator<T> (T,T) T 二元函数,输入输出类型相同
Function<T,R> 使用说明:当铺,用东西换钱。
先看源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @FunctionalInterface public interface Function <T , R > { R apply (T t) ; default <V> Function<V, R> compose (Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } default <V> Function<T, V> andThen (Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } static <T> Function<T, T> identity () { return t -> t; } }
实践 apply
方法,处理传入的参数并返回。
1 2 3 4 public void doubleNum () { Function<Integer, String> d = integer -> String.valueOf(2 * integer); System.out.println(d.apply(10 )); }
compose
方法,是传入一个前置处理的函数,前置函数的返回结果是 apply
的入参。
1 2 3 4 5 6 7 8 9 10 11 12 public void compose () { Function<Integer, String> d = integer -> { System.out.println("real double apply begins, param is " + integer); return String.valueOf(2 * integer); }; Function<String, String> compose = d.compose(s -> { System.out.println("compose begins, str to integer, str is " + s); return Integer.valueOf(s); }); System.out.println("final result is " + compose.apply("10" )); }
andThen
方法,传入一个后置函数,可以形成链式处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public void andThen () { Function<Integer, String> d = integer -> { System.out.println("real double apply begins, param is " + integer); return String.valueOf(2 * integer); }; Function<Integer, String> andThen = d.andThen(s -> { System.out.println("and then begins" ); return "after and then, result is " + s; }); System.out.println(andThen.apply(10 )); }
identity
,是 t -> t
的一种替代式写法。
1 2 3 4 5 6 7 8 9 public void identify () { List<Student> list = new ArrayList<>(); list.add(new Student("1" , "张三" )); list.add(new Student("2" , "李四" )); Map<String, Student> map1 = list.stream().collect(Collectors.toMap(Student::getId, s -> s)); Map<String, Student> map2 = list.stream().collect(Collectors.toMap(Student::getId, Function.identity())); }
其他类型 Function 接口 函数 说明 BiFunction R apply(T t, U u) 二元入参 DoubleFunction R apply(double value) 输入为double DoubleToIntFunction int applyAsInt(double value) 输入double,输出int DoubleToLongFunction long applyAsLong(double value) 输入double,输入long IntFunction R apply(int value) 输入为int IntToDoubleFunction double applyAsDouble(int value) 输入int,输出double IntToLongFunction long applyAsLong(int value) 输入int,输出long LongFunction R apply(long value) 输入为long LongToDoubleFunction double applyAsDouble(long value) 输入long,输出double LongToIntFunction int applyAsInt(long value) 输入long,输出int ToLongFunction long applyAsLong(T value) 输出为long ToLongBiFunction long applyAsLong(T t, U u) 二元入参,输出为long ToIntFunction int applyAsInt(T value) 输出为int ToIntBiFunction int applyAsInt(T t, U u) 二元入参,输出为int ToDoubleFunction double applyAsDouble(T value) 输出为long ToDoubleBiFunction double applyAsDouble(T t, U u) 二元入参,输出为long
Consumer<T> 使用说明:貔貅,光进不出。
先看源码 1 2 3 4 5 6 7 8 9 10 11 @FunctionalInterface public interface Consumer <T > { void accept (T t) ; default Consumer<T> andThen (Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
实践 Consumer
主要是用于提供一个可以对入参处理的函数,比如用在 foreach
。
1 2 3 4 5 6 7 8 public static void main (String[] args) { Consumer<String> c1 = str -> System.out.println("this is the first time, word is " + str); Consumer<String> c2 = str -> System.out.println("this is the second time, word is " + str); List<String> stringList = Arrays.asList("I" , "am" , "the" , "demo" ); stringList.forEach(c1); stringList.forEach(c1.andThen(c2)); }
其他类型 Consumer 接口 函数 说明 BiConsumer accept(T t, U u) 二元入参 DoubleConsumer void accept(double value) 入参为double IntConsumer void accept(int value) 入参为int LongConsumer void accept(long value) 入参为long ObjDoubleConsumer void accept(T t, double value) 后参为double ObjIntConsumer void accept(T t, int value) 后参为int ObjLongConsumer void accept(T t, long value) 后参为long
Supplier<T> 使用说明:老好人,散财童子,啥都不要。
先看源码 Supplier
的源码就比较简单了,只有一个 get
方法,返回指定类型对象。
1 2 3 4 @FunctionalInterface public interface Supplier <T > { T get () ; }
实践 1 2 3 4 5 6 7 8 public void demo () { Supplier<Double> random = Math::random; System.out.println(random.get()); Supplier<String> s = String::new ; String result = Stream.of("this" , "is" , "is" , "a" , "demo" ).findFirst().orElseGet(s); System.out.println(result); }
其他类型 Supplier 接口 函数 说明 BooleanSupplier boolean getAsBoolean() 返回boolean DoubleSupplier double getAsDouble() 返回double IntSupplier int getAsInt() 返回int LongSupplier long getAsLong() 返回long
Predicate<T> 使用说明:预言家,判断你是不是狼人。
先看源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @FunctionalInterface public interface Predicate <T > { boolean test (T t) ; default Predicate<T> and (Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); } default Predicate<T> negate () { return (t) -> !test(t); } default Predicate<T> or (Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); } static <T> Predicate<T> isEqual (Object targetRef) { return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object); } }
实践 Predicate
主要用于有布尔类型的场景。
1 2 3 4 5 6 7 8 9 10 public static void demo () { Predicate<Integer> p1 = integer -> integer > 10 ; Predicate<Integer> p2 = integer -> integer < 5 ; System.out.println("7 > 10 :" + p1.test(7 )); System.out.println("7 !< 5 :" + p2.negate().test(7 )); System.out.println("7 > 10 && 7 < 5 : " + p1.and(p2).test(7 )); System.out.println("0 > 10 || 0 < 5 : " + p1.or(p2).test(0 )); System.out.println("null equals null : " + Predicate.isEqual(null ).test(null )); }
其他类型 Predicate 接口 函数 说明 BiPredicate boolean test(T t, U u) 二元入参 DoublePredicate boolean test(double value) 入参double IntPredicate boolean test(int value) 入参返回int LongPredicate boolean test(long value) 入参返回long
UnaryOperator<T> 使用说明:汇率结算,用钱换钱。
先看源码 UnaryOperator
继承 Function
,只不过是输入输出类型相同。
1 2 3 4 5 public interface UnaryOperator <T > extends Function <T , T > { static <T> UnaryOperator<T> identity () { return t -> t; } }
实践 1 2 3 4 5 6 7 public void unaryDemo () { Function<Integer, Integer> squareFunc = integer -> integer * integer; UnaryOperator<Integer> squareUnary = integer -> integer * integer; System.out.println(squareFunc.apply(9 )); System.out.println(squareUnary.apply(9 )); }
BinaryOperator<T> 使用说明:合成机器,两个宝石合成一个宝石。
先看源码 BinaryOperator
继承自 BiFunction
,只不过是两个入参以及返回类型相同。
同时,BinaryOperator
提供了 minBy
和 maxBy
两个静态方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @FunctionalInterface public interface BinaryOperator <T > extends BiFunction <T ,T ,T > { public static <T> BinaryOperator<T> minBy (Comparator<? super T> comparator) { Objects.requireNonNull(comparator); return (a, b) -> comparator.compare(a, b) <= 0 ? a : b; } public static <T> BinaryOperator<T> maxBy (Comparator<? super T> comparator) { Objects.requireNonNull(comparator); return (a, b) -> comparator.compare(a, b) >= 0 ? a : b; } }
实践 比如整数四则运算,两个输入和输出是一样的类型。
1 2 3 4 public void multiply () { BinaryOperator<Integer> multiply = (i1, i2) -> i1 * i2; System.out.println(multiply.apply(3 , 5 )); }
minBy
和 maxBy
,按比较器输出。
1 2 3 4 5 6 public void compare () { BinaryOperator<Integer> min = BinaryOperator.minBy(Comparator.comparingInt(i -> i)); BinaryOperator<Integer> max = BinaryOperator.maxBy(Comparator.comparingInt(i -> i)); System.out.println(min.apply(1 ,2 )); System.out.println(max.apply(1 ,2 )); }
总结 函数式接口,归根究底还是接口,定义的是标准行为。但是函数式接口又只有一个抽象函数,使得我们可以不关注函数名字到底是什么,而专注于业务逻辑。
JDK 提供的这一套函数式接口,全部在 java.util.function
包中,基本上可以满足我们常用场景的使用需求。这些接口有些抽象,但只要将关注点转移到这个接口到底能干什么,就不再难理解了。当然,如果还有特殊的使用场景,可以通过自定义函数式接口解决。
注:本文配套代码可在 github
查看:stream-and-lambda