其他网址
JDK8函数式接口Function、Consumer、Predicate、Supplier_酒肉猿-CSDN博客
Java8中Function函数式接口详解及使用_Life is for sharing的博客-CSDN博客
JAVA8的java.util.function包讲解 (方便的函数式编程)_我是guyue,guyue就是我O(∩_∩)O-CSDN博客
《JAVA开发实战经典 第2版》=> 16.10 内建函数式接口
简介
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 >
功能型接口 接收T对象,返回R对象。
Consumer< T >
消费型接口 接收T对象,不返回值
Supplier< T >
供给型接口 不接收值,返回T对象
Predicate< 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对象
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; } }
所有接口
接口定义
描述
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结果的函数
实例
apply
Function<String, String> function = a -> a + " Jack!"; System.out.println(function.apply("Hello")); // 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!
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); }; } }
所有接口
接口
描述
Consumer<T>
提供一个T类型的输入参数,不返回执行结果
BiConsumer<T,U>
提供两个自定义类型的输入参数,不返回执行结果
DoubleConsumer
表示接受单个double值参数,但不返回结果的操作
IntConsumer
表示接受单个int值参数,但不返回结果的操作
LongConsumer
表示接受单个long值参数,但不返回结果的操作
ObjDoubleConsumer<T>
表示接受object值和double值,但是不返回任何操作结果
ObjIntConsumer<T>
表示接受object值和int值,但是不返回任何操作结果
ObjLongConsumer<T>
表示接受object值和long值,但是不返回任何操作结果
实例
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!
Supplier< T > 供给型
简介
概述
作用
创建一个对象(工厂类)
使用场景
Optional.orElseGet(Supplier<? extends T>):当this对象为null,就通过传入supplier创建一个T返回。
设计思想
封装工厂创建对象的逻辑
定义
package java.util.function; @FunctionalInterface public interface Supplier<T> { //获取结果值 T get(); }
不常用接口
接口
描述
Supplier<T>
不提供输入参数,但是返回结果的函数
BooleanSupplier
不提供输入参数,但是返回boolean结果的函数
DoubleSupplier
不提供输入参数,但是返回double结果的函数
IntSupplier
不提供输入参数,但是返回int结果的函数
LongSupplier
不提供输入参数,但是返回long结果的函数
实例
get
Supplier<String> supplier = () -> "Hello Jack!"; System.out.println(supplier.get()); // Hello Jack!
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); } }
所有接口
接口
描述
Predicate<T>
对给定的输入参数执行操作,返回一个boolean类型的结果(布尔值函数)
BiPredicate<T,U>
对给定的两个输入参数执行操作,返回一个boolean类型的结果(布尔值函数)
DoublePredicate
对给定的double参数执行操作,返回一个boolean类型的结果(布尔值函数)
IntPredicate
对给定的int输入参数执行操作,返回一个boolean类型的结果(布尔值函数)
LongPredicate
对给定的long参数执行操作,返回一个boolean类型的结果(布尔值函数)
实例
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
原理
其他网址
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));