0
点赞
收藏
分享

微信扫一扫

Java--函数式接口(java.util.function包)

简单聊育儿 2022-03-23 阅读 64



简介

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 >



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对象


Function< T, R >  功能型

简介

概述

作用

实现一个”一元函数“,即传入一个值经过函数的计算返回另一个值。

使用场景

  1. V HashMap.computeIfAbsent(K , Function<K, V>) // 简化代码,如果指定的键尚未与值关联或与null关联,使用函数返回值替换。
  2. <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对象进行迭代。

设计思想

  1. 开发者调用ArrayList.forEach时,一般希望自定义遍历的消费逻辑,比如:输出日志或者运算处理等。
  2. 处理逻辑留给使用者,使用灵活多变。
  3. 多变的逻辑能够封装成一个类(实现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));

其他网址


《JAVA开发实战经典 第2版》=> 16.10 内建函数式接口


举报

相关推荐

0 条评论