简介
本文介绍Java的函数式接口(java.util.function包)。
常用接口
接口定义 | 方法 | 作用 | 说明 |
Function< T, R > | R apply(T t); | 参数:T对象。返回值:R对象。 | 功能型接口 |
Consumer< T > | void accept(T t); | 参数:T对象。返回值:无 | 消费型接口 |
Supplier< T > | T get(); | 参数:无。返回值:T对象 | 供给型接口 |
Predicate< T > | boolean test(T t); | 参数:T对象。返回值:boolean | 断言型接口 |
不常用接口
接口定义 | 描述 |
UnaryOperator< T > | 参数:T对象。返回值:T对象 |
BiConsumer<T, U> | 参数:T对象和U对象。返回值:无 |
BiPredicate<T, U> | 参数:T对象和U对象。返回值:boolean |
BiFunction<T, U, R> | 参数:T对象和U对象。返回值:R对象 |
BinaryOperator< T > | 参数:两个T对象。参会之:T对象 |
JDK1.8函数接口变化
JDK1.8新增加的函数接口:
- java.util.function
- java.util.stream
JDK1.8之前已有的函数式接口:
- java.lang.Runnable
- java.util.concurrent.Callable
- java.security.PrivilegedAction
- java.util.Comparator
- java.io.FileFilter
- java.nio.file.PathMatcher
- java.lang.reflect.InvocationHandler
- java.beans.PropertyChangeListener
- java.awt.event.ActionListener
- javax.swing.event.ChangeListener
Function< T, R > 功能型
简介
概述
作用
实现一个”一元函数“,即传入一个值经过函数的计算返回另一个值。
使用场景
- V HashMap.computeIfAbsent(K , Function<K, V>) // 简化代码,如果指定的键尚未与值关联或与null关联,使用函数返回值替换。
- <R> Stream<R> map(Function<? super T, ? extends R> mapper); // 转换流
设计思想
一元函数的思想,将转换逻辑提取出来,解耦合
定义
package java.util.function; import java.util.Objects; @FunctionalInterface public interface Function<T, R> { // 接受输入参数,对输入执行所需操作后 返回一个结果。 R apply(T t); // 返回一个 先执行before函数对象apply方法,再执行当前函数对象apply方法的 函数对象。 default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } // 返回一个 先执行当前函数对象apply方法, 再执行after函数对象apply方法的 函数对象。 default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } // 返回一个执行了apply()方法之后只会返回输入参数的函数对象。 static <T> Function<T, T> identity() { return t -> t; } }
实例
apply示例1:直接返回
Function<String, String> function = a -> "Hello," + a; System.out.println(function.apply("Jack")); // Hello,Jack
apply示例2:执行代码再返回
Function<String, String> function = a -> { System.out.print("Welcome!"); return "Hello," + a; } System.out.println(function.apply("Jack")); // Welcome!Hello,Jack
compose
Function<String, String> function = a -> {a + " Jack!"}; Function<String, String> function1 = a -> {a + " Bob!"}; String greet = function.compose(function1).apply("Hello"); System.out.println(greet); // Hello Bob! Jack!
andThen
Function<String, String> function = a -> {a + " Jack!"}; Function<String, String> function1 = a -> {a + " Bob!"}; String greet = function.andThen(function1).apply("Hello"); System.out.println(greet); // Hello Jack! Bob!
所有接口
接口定义 | 描述 |
Function< T, R > | 接收T对象,返回R对象。 |
DoubleFunction<R> | 接收一个double类型的参数并返回结果的函数 |
DoubleToIntFunction | 接收一个double类型的参数并返回int结果的函数 |
DoubleToLongFunction | 接收一个double类型的参数并返回long结果的函数 |
IntFunction<R> | 接收一个int类型的参数并返回结果的函数 |
IntToDoubleFunction | 接收一个int类型的参数并返回double结果的函数 |
IntToLongFunction | 接收一个int类型的参数并返回long结果的函数 |
LongFunction<R> | 接收一个long类型的参数并返回结果的函数 |
LongToDoubleFunction | 接收一个long类型的参数并返回double结果的函数 |
LongToIntFunction | 接收一个long类型的参数并返回int结果的函数 |
ToDoubleBiFunction<T,U> | 接收两个参数并返回double结果的函数 |
ToDoubleFunction<T> | 接收一个参数并返回double结果的函数 |
ToIntBiFunction<T,U> | 接收两个参数并返回int结果的函数 |
ToIntFunction<T> | 接收一个参数并返回int结果的函数 |
ToLongBiFunction<T,U> | 接收两个参数并返回long结果的函数 |
ToLongFunction<T> | 接收一个参数并返回long结果的函数 |
Consumer< T > 消费型
简介
概述
作用
消费某个对象
使用场景
Iterable接口的forEach方法需要传入Consumer,大部分集合类都实现了该接口,用于返回Iterator对象进行迭代。
设计思想
- 开发者调用ArrayList.forEach时,一般希望自定义遍历的消费逻辑,比如:输出日志或者运算处理等。
- 处理逻辑留给使用者,使用灵活多变。
- 多变的逻辑能够封装成一个类(实现Consumer接口),将逻辑提取出来。
定义
package java.util.function; import java.util.Objects; @FunctionalInterface public interface Consumer<T> { //提供一个T类型的输入参数,不返回执行结果 void accept(T t); //返回一个组合函数,after将会在该函数执行之后应用 default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
实例
accept
StringBuilder sb = new StringBuilder("Hello "); Consumer<StringBuilder> consumer = (str) -> str.append("Jack!"); consumer.accept(sb); System.out.println(sb.toString()); // Hello Jack!
andThen
StringBuilder sb = new StringBuilder("Hello "); Consumer<StringBuilder> consumer = (str) -> str.append("Jack!"); Consumer<StringBuilder> consumer1 = (str) -> str.append(" Bob!"); consumer.andThen(consumer1).accept(sb); System.out.println(sb.toString()); // Hello Jack! Bob!
所有接口
接口 | 描述 |
Consumer<T> | 提供一个T类型的输入参数,不返回执行结果 |
BiConsumer<T,U> | 提供两个自定义类型的输入参数,不返回执行结果 |
DoubleConsumer | 表示接受单个double值参数,但不返回结果的操作 |
IntConsumer | 表示接受单个int值参数,但不返回结果的操作 |
LongConsumer | 表示接受单个long值参数,但不返回结果的操作 |
ObjDoubleConsumer<T> | 表示接受object值和double值,但是不返回任何操作结果 |
ObjIntConsumer<T> | 表示接受object值和int值,但是不返回任何操作结果 |
ObjLongConsumer<T> | 表示接受object值和long值,但是不返回任何操作结果 |
Supplier< T > 供给型
简介
概述
作用
创建一个对象(工厂类)
使用场景
Optional.orElseGet(Supplier<? extends T>):当this对象为null,就通过传入supplier创建一个T返回。
设计思想
封装工厂创建对象的逻辑
定义
package java.util.function; @FunctionalInterface public interface Supplier<T> { //获取结果值 T get(); }
实例(常用)
示例1:直接返回值
Supplier<String> supplier = () -> "Hello Jack!"; System.out.println(supplier.get()); // Hello Jack!
示例2:执行代码
Supplier<String> supplier = () -> { int i = 3; return i + 1; } System.out.println(supplier.get()); // 4
示例3:封装功能
假如我们有好几个地方需要手动捕获异常,然后打印出来,正常写法:每段代码都try catch一下。这样会很麻烦!方案是:封装成Supplier,然后调用:
package com.example.a; import java.util.function.Supplier; public class Demo { public static void main(String[] args) { // 原始语句:Integer i = 2 / 0; Integer i = executeCatchException(() -> { return 2 / 0; }); System.out.println(i); Integer a = null; // 原始语句:Boolean result = a.equals(2); Boolean result = executeCatchException(() -> { return a.equals(2); }); System.out.println(a); } private static <T> T executeCatchException(Supplier<T> supplier) { T t = null; try { t = supplier.get(); } catch (Exception e) { String msg = "报错:" + e.getMessage(); System.out.println(msg); } return t; } }
结果
报错:/ by zero null 报错:null null
所有接口
接口 | 描述 |
Supplier<T> | 不提供输入参数,但是返回结果的函数 |
BooleanSupplier | 不提供输入参数,但是返回boolean结果的函数 |
DoubleSupplier | 不提供输入参数,但是返回double结果的函数 |
IntSupplier | 不提供输入参数,但是返回int结果的函数 |
LongSupplier | 不提供输入参数,但是返回long结果的函数 |
Predicate< T > 断言型接口
简介
概述
作用
- 判断对象是否符合某个条件
使用场景
- ArrayList的removeIf(Predicate):删除符合条件的元素
- 如果条件硬编码在ArrayList中,它将提供无数的实现,但是如果让调用者传入条件,这样ArrayList就可以从复杂和无法猜测的业务中解放出来。
设计思想
- 提取条件,让条件从处理逻辑脱离出来,解耦合
定义
package java.util.function; import java.util.Objects; @FunctionalInterface public interface Predicate<T> { // 根据给定的参数进行判断 boolean test(T t); // 返回一个组合判断,将other以短路与的方式加入到函数的判断中 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); } // 返回一个组合判断,将other以短路或的方式加入到函数的判断中 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); } }
实例
test
Predicate<Integer> predicate = number -> number != 0; System.out.println(predicate.test(10)); //true
and
Predicate<Integer> predicate = number -> number != 0; predicate = predicate.and(number -> number >= 10); System.out.println(predicate.test(10)); //true
or
Predicate<Integer> predicate = number -> number != 0; predicate = predicate.or(number -> number != 10); System.out.println(predicate.test(10)); //true
所有接口
接口 | 描述 |
Predicate<T> | 对给定的输入参数执行操作,返回一个boolean类型的结果(布尔值函数) |
BiPredicate<T,U> | 对给定的两个输入参数执行操作,返回一个boolean类型的结果(布尔值函数) |
DoublePredicate | 对给定的double参数执行操作,返回一个boolean类型的结果(布尔值函数) |
IntPredicate | 对给定的int输入参数执行操作,返回一个boolean类型的结果(布尔值函数) |
LongPredicate | 对给定的long参数执行操作,返回一个boolean类型的结果(布尔值函数) |
原理
apply
首先我们已经知道了Function是一个泛型类,其中定义了两个泛型参数T和R,在Function中,T代表输入参数,R代表返回的结果。也许你很好奇,为什么跟别的java源码不一样,Function 的源码中并没有具体的逻辑呢?
其实这很容易理解,Function 就是一个函数,其作用类似于数学中函数的定义:y = f(x) ,(x,y)跟<T,R>的作用几乎一致。所以Function中没有具体的操作,具体的操作需要我们指定,apply具体返回的结果取决于传入的lambda表达式。
举例:
Function<Integer, Integer> test = i -> i + 1; System.out.println(test.apply(5));
用lambda表达式定义了一个行为使得i自增1,我们使用参数5执行apply,最后返回6。这跟我们以前看待Java的眼光已经不同了,在函数式编程之前我们定义一组操作首先想到的是定义一个方法,然后指定传入参数,返回我们需要的结果。函数式编程的思想是先不去考虑具体的行为,而是先去考虑参数,具体的方法我们可以后续再设置。
再举个例子:
public void test() { Function<Integer, Integer> test1 = i -> i + 1; Function<Integer, Integer> test2 = i -> i * i; /* print:6 */ System.out.println(calculate(test1, 5)); /* print:25 */ System.out.println(calculate(test2, 5)); } public static Integer calculate(Function<Integer, Integer> test, Integer number) { return test.apply(number); }
通过传入不同的Function,实现了在同一个方法中实现不同的操作。在实际开发中这样可以大大减少很多重复的代码,比如我在实际项目中有个新增用户的功能,但是用户分为VIP和普通用户,且有两种不同的新增逻辑。那么此时我们就可以先写两种不同的逻辑。除此之外,这样还让逻辑与数据分离开来,我们可以实现逻辑的复用。
当然实际开发中的逻辑可能很复杂,比如两个方法F1,F2都需要操作AB,但是F1需要A->B,F2方法需要B->A。这样的我们用刚才的方法也可以实现,源码如下:
public void test() { Function<Integer, Integer> A = i -> i + 1; Function<Integer, Integer> B = i -> i * i; /* F1:36 */ System.out.println("F1:" + B.apply(A.apply(5))); /* F2:26 */ System.out.println("F2:" + A.apply(B.apply(5))); }
compose和andThen
如上边例子所示,假如我们F1,F2需要四个逻辑ABCD,那我们还这样写就会变得很麻烦了。compose和andThen可以解决我们的问题。
先看compose的源码:compose接收一个Function参数,返回时先用传入的逻辑执行apply,然后使用当前Function的apply。如下所示
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); }
andThen跟compose正相反,先执行当前的逻辑,再执行传入的逻辑:
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); }
这样说可能不够直观,我可以换个说法:compose等价于B.apply(A.apply(5)),而andThen等价于A.apply(B.apply(5))。
Function<Integer, Integer> A = i -> i + 1; Function<Integer, Integer> B = i -> i * i; /* F1:36 */ System.out.println("F1:" + B.apply(A.apply(5))); /* F1:36 */ System.out.println("F1:" + B.compose(A).apply(5)); /* F2:26 */ System.out.println("F2:" + A.apply(B.apply(5))); /* F2:26 */ System.out.println("F2:" + B.andThen(A).apply(5));
其他网址
《JAVA开发实战经典 第2版》=> 16.10 内建函数式接口
请先
!