优雅编程之函数式接口
  0ay4xuMOEQMf 2023年11月02日 52 0


函数式接口 (Functional Interface) 就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为 lambda 表达式。Java 允许利用 Lambda 表达式创建这些接口的实例。java.util.function 包是 Java 8 增加的一个新技术点 “函数式接口”,此包共有 43 个接口。别指望能够全部记住他们,但是如果能记住其中 6 个基础接口,必要时就可以推断出其余接口了。这些接口是为了使 Lamdba 函数表达式使用的更加简便,当然你也可以自己自定义接口来应用于 Lambda 函数表达式。

JDK 1.8 API 包含了很多内建的函数式接口,比如 Comparator 或者 Runnable 接口,这些接口都增加了 @FunctionalInterface 注解以便能用在 Lamdba 上。现如今,我们则从 Function 常用函数入口,真正了解一下函数式接口。

Java 8 中函数式接口

接口

描述

函数签名

范例

UnaryOperator<T>

接收 T 对象,返回 T 对象

T apply(T t)

String::toLowerCase

BinaryOprator<T>

接收两个 T 对象,返回 T 对象

T apply(T t1, T t2)

BigInteger::add

Predicate<T>

接收 T 对象,返回 boolean

boolean test(T t)

Collection::isEmpty

Function<T, R>

接收 T 对象,返回 R 对象

R apply(T t)

Arrays::asList

Supplier<T>

提供 T 对象(例如工厂),不接收值

T get()

Instant::new

Consumer<T>

接收 T 对象,不返回值

void accept(T t)

System.out::println

标注为 @FunctionalInterface 的接口被称为函数式接口,该接口有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。是否是一个函数式接口,需要注意的有以下几点:

  • 该注解只能标记在“有且仅有一个抽象方法”的接口上。
  • Java 8 接口中的静态方法和默认方法,都不算是抽象方法。
  • 接口默认继承 java.lang.Object,所以如果接口显示声明覆盖了 Object 中方法,那么也不算抽象方法。
  • 该注解不是必须的,如果一个接口符合 “函数式接口” 定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了 @FunctionInterface,那么编译器会报错。
  • 在一个接口中定义两个自定义的方法,就会产生 Invalid ‘@FunctionalInterface’ annotation; FunctionalInterfaceTest is not a functional interface 错误。
@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

Consumer:消费型接口(void accept(T t))

函数式接口

描述

Consumer<T>

提供一个 T 类型的输入参数,不返回执行结果

BiConsumer<T, U>

提供两个自定义类型的输入参数,不返回执行结果

DoubleConsumer

提供一个 double 类型的输入参数,不返回执行结果

IntConsumer

提供一个 int 类型的输入参数,不返回执行结果

LongConsumer

提供一个 long 类型的输入参数,不返回执行结果

ObjDoubleConsumer<T>

提供一个 double 类型的输入参数和一个 T 类型的输入参数,不返回执行结果

ObjIntConsumer<T>

提供一个 int 类型的输入参数和一个 T 类型的输入参数,不返回执行结果

ObjLongConsumer<T>

提供一个 long 类型的输入参数和一个 T 类型的输入参数,不返回执行结果

(1)作用:消费某个对象

(2)使用场景:Iterable 接口的 forEach 方法需要传入 Consumer,大部分集合类都实现了该接口,用于返回 Iterator 对象进行迭代。

(3)主要方法

方法

描述

void accept(T t)

对给定的参数执行操作

default Consumer<T> andThen(Consumer< ? super T> after)

返回一个组合函数,after 将会在该函数执行之后应用

(4)代码示例

Consumer<T>:提供一个 T 类型的输入参数,不返回执行结果

@Test
public void testConsumer() {
    // Consumer<T>:accept(T t)
    Consumer<String> consumer = System.out::println;
    consumer.accept("hello world!");    // hello world!

    // Consumer<T>:andThen(Consumer<? super T> after) -> 返回一个组合函数,after将会在该函数执行之后应用
    StringBuilder sb = new StringBuilder("Hello ");
    Consumer<StringBuilder> consumer_accept = (str) -> str.append("Jack! ");
    Consumer<StringBuilder> consumer_andThen = (str) -> str.append("Bob!");
    consumer_accept.andThen(consumer_andThen).accept(sb);
    System.out.println(sb.toString());  // Hello Jack! Bob!
}

BiConsumer<T, U> :提供两个自定义类型的输入参数,不返回执行结果

@Test
public void testBiConsumer() {
    // BiConsumer<T, U>:accept(T t, U u)
    BiConsumer<String, String> biConsumer = (a, b) -> System.out.printf("%s %s!", a, b);
    biConsumer.accept("hello", "world");    // hello world!
}

DoubleConsumer :提供一个 double 类型的输入参数,不返回执行结果

@Test
public void testDoubleConsumer() {
    // DoubleConsumer:accept(double value)
    DoubleConsumer doubleConsumer = System.out::println;
    doubleConsumer.accept(9.12D);   // 9.12
}

ObjDoubleConsumer<T> :提供一个 double 类型的输入参数和一个 T 类型的输入参数,不返回执行结果

@Test
public void testObjDoubleConsumer() {
    // ObjDoubleConsumer<T>:accept(T t, double value)
    ObjDoubleConsumer<String> stringObjDoubleConsumer = (s, value) -> System.out.println(s + value);
    stringObjDoubleConsumer.accept("金额:", 9.12D);   // 金额:9.12
}

Predicate:断言型接口(boolean test(T t))

函数式接口

描述

Predicate<T>

提供一个 T 类型的输入参数,返回一个 boolean 类型的结果

BiPredicate<T,U>

提供两个自定义类型的输入参数,返回一个 boolean 类型的结果

DoublePredicate

提供一个 double 类型的输入参数,返回一个 boolean 类型的结果

IntPredicate

提供一个 int 类型的输入参数,返回一个 boolean 类型的结果

LongPredicate

提供一个 long 类型的输入参数,返回一个 boolean 类型的结果

(1)作用:判断对象是否符合某个条件

(2)使用场景:ArrayList 的 removeIf(Predicate):删除符合条件的元素。如果条件硬编码在 ArrayList 中,它将提供无数的实现,但是如果让调用者传入条件,这样 ArrayList 就可以从复杂和无法猜测的业务中解放出来。

(3)主要方法

方法

描述

boolean test(T t)

根据给定的参数进行判断

Predicate<T> and(Predicate< ? super T> other)

返回一个组合判断,将 other 以短路并且的方式加入到函数的判断中

Predicate<T> or(Predicate< ? super T> other)

返回一个组合判断,将 other 以短路或的方式加入到函数的判断中

Predicate<T> negate()

将函数的判断取反

(4)代码示例

Predicate<T> :提供一个 T 类型的输入参数,返回一个 boolean 类型的结果

@Test
public void testPredicate() {
    // Predicate<T>:boolean test(T t)
    Predicate<List<String>> listPredicate = Collection::isEmpty;
    System.out.println(listPredicate.test(Arrays.asList("Hello", "World"))); // false

    // Predicate<T>:boolean test(T t)
    Predicate<Integer> predicate = integer -> integer != 0;
    System.out.println(predicate.test(10)); // true

    // Predicate<T>:Predicate<T> and(Predicate<? super T> other)
    predicate = predicate.and(integer -> integer >= 10);
    System.out.println(predicate.test(10)); // true

    // Predicate<T>:Predicate<T> or(Predicate<? super T> other)
    predicate = predicate.or(integer -> integer != 10);
    System.out.println(predicate.test(10)); // true

    // Predicate<T>:Predicate<T> negate()
    predicate = predicate.negate();
    System.out.println(predicate.test(10)); // false
}

Function:函数型接口(R apply(T t))

函数式接口

描述

Function<T, R>

提供一个 T 类型的输入参数,返回一个 R 类型的结果

BiFunction<T, U, R>

提供两个自定义类型的输入参数,返回一个 R 类型的结果

DoubleFunction<R>

提供一个 double 类型的输入参数,返回一个 R 类型的结果

DoubleToIntFunction

提供一个 double 类型的输入参数,返回一个 int 类型的结果

DoubleToLongFunction

提供一个 double 类型的输入参数,返回一个 long 类型的结果

IntFunction<R>

提供一个 int 类型的输入参数,返回一个 R 类型的结果

IntToDoubleFunction

提供一个 int 类型的输入参数,返回一个 double 类型的结果

IntToLongFunction

提供一个 int 类型的输入参数,返回一个 long 类型的结果

LongFunction<R>

提供一个 long 类型的输入参数,返回一个 R 类型的结果

LongToDoubleFunction

提供一个 long 类型的输入参数,返回一个 double 类型的结果

LongToIntFunction

提供一个 long 类型的输入参数,返回一个 int 类型的结果

ToDoubleBiFunction<T, U>

提供两个自定义类型的输入参数,返回一个 double 类型的结果

ToDoubleFunction<T>

提供一个 T 类型的输入参数,返回一个 double 类型的结果

ToIntBiFunction<T, U>

提供两个自定义类型的输入参数,返回一个 int 类型的结果

ToIntFunction<T>

提供一个 T 类型的输入参数,返回一个 int 类型的结果

ToLongBiFunction<T, U>

提供两个自定义类型的输入参数,返回一个 long 类型的结果

ToLongFunction<T>

提供一个 T 类型的输入参数,返回一个 long 类型的结果

(1)作用:实现一个”一元函数“,即传入一个值经过函数的计算返回另一个值。

(2)使用场景:V HashMap.computeIfAbsent(K , Function<K, V>):如果指定的 key 不存在或相关的 value 为 null 时,设置 key 与关联一个计算出的非 null 值,计算出的值为 null 的话什么也不做(不会去删除相应的 key)。如果 key 存在并且对应 value 不为 null 的话什么也不做。

(3)主要方法

方法

描述

R apply(T t)

将此参数应用到函数中

Function<T, V> andThen(Function< ? super R, ? extends V> after)

返回一个组合函数,该函数结果应用到 after 函数中

Function<V, R> compose(Function< ? super V, ? extends T> before)

返回一个组合函数,首先将入参应用到 before 函数,再将 before 函数结果应用到该函数中

(4)代码示例

Function<T, R> :提供一个 T 类型的输入参数,返回一个 R 类型的结果

@Test
public void testFunction() {
    // Function<T, R>:R apply(T t)
    Function<String[], List<String>> asList = Arrays::asList;
    System.out.println(asList.apply(new String[]{"Hello", "World"}));   // [Hello, World]

    Function<String, String> function = s -> String.format("%s, Jack!", s);
    Function<String, String> compose = s -> StringUtils.isEmpty(s) ? "Hello" : s;
    Function<String, String> andThen = String::toUpperCase;

    String s = function.compose(compose).andThen(andThen).apply("");
    System.out.println(s);  // HELLO, JACK!
}

Supplier:供给型接口(R apply(T t))

函数式接口

描述

Supplier<T>

不提供输入参数,返回一个 T 类型的结果

BooleanSupplier

不提供输入参数,返回一个 boolean 类型的结果

DoubleSupplier

不提供输入参数,返回一个 double 类型的结果

IntSupplier

不提供输入参数,返回一个 int 类型的结果

LongSupplier

不提供输入参数,返回一个 long 类型的结果

(1)作用:创建一个对象(工厂类)

(2)使用场景:Optional.orElseGet(Supplier< ? extends T>):当 this 对象为 null,就通过传入 supplier 创建一个 T 返回。

(3)主要方法

方法

描述

T get()

获取结果值

(4)代码示例

Supplier<T> :不提供输入参数,返回一个 T 类型的结果

@Test
public void testSupplier() {
    // Supplier<T>:T get();
    Supplier<String> supplier = () -> "Hello Jack!";
    System.out.println(supplier.get()); // Hello Jack!
}

Operator:操作型接口(T apply(T t))

函数式接口

描述

UnaryOperator<T>

提供一个 T 类型的输入参数,返回一个 T 类型的结果

BinaryOperator<T>

提供两个 T 类型的输入参数,返回一个 T 类型的结果

DoubleBinaryOperator

提供两个 double 类型的输入参数,返回两个 double 类型的结果

DoubleUnaryOperator

提供一个 double 类型的输入参数,返回一个 double 类型的结果

IntBinaryOperator

提供两个 int 类型的输入参数,返回一个 int 类型的结果

IntUnaryOperator

提供一个 int 类型的输入参数,返回一个 int 类型的结果

LongBinaryOperator

提供两个 long 类型的输入参数,返回一个 long 类型的结果

LongUnaryOperator

提供一个 long 类型的输入参数,返回一个 long 类型的结果

(1)作用:实现一个”一元函数“,即传入一个值经过函数的计算返回另一个同类型的值。

(2)使用场景:UnaryOperator 继承了 Function,与 Function 作用相同,不过 UnaryOperator,限定了传入类型和返回类型必需相同。

(3)主要方法

方法

描述

T apply(T t)

将给定参数应用到函数中

Function<T, V> andThen(Function< ? super T, ? extends V> after)

返回一个组合函数,该函数结果应用到 after 函数中

Function<V, T> compose(Function< ? super V, ? extends T> before)

返回一个组合函数,首先将入参应用到 before 函数,再将 before 函数结果应用到该函数中

(4)代码示例

UnaryOperator<T> :提供一个 T 类型的输入参数,返回一个 T 类型的结果

@Test
public void testOperator() {
    // UnaryOperator<T>:T apply(T t)
    UnaryOperator<String> unaryOperator = String::toUpperCase;
    System.out.println(unaryOperator.apply("Hello World!"));    // HELLO WORLD!
}

BinaryOperator<T> :提供两个 T 类型的输入参数,返回一个 T 类型的结果

@Test
public void testBinaryOperator() {
    // BinaryOperator<T>:T apply(T t1, T T2);
    BinaryOperator<BigInteger> binaryOperator = BigInteger::add;
    System.out.println(binaryOperator.apply(BigInteger.ONE, BigInteger.TEN));
}

总结

java.util.function 包已经为大家提供了大量标注的函数接口。只要标准的函数接口能够满足需求,通常应该优先考虑,而不是专门再构建一个新的函数接口。这样会使 API 更加容易学习,通过减少它的概念内容,显著提升互操作性优势,因为许多标准的函数接口都提供了有用的默认方法。


参考博文

[1]. JDK8 新特性 - java.util.function-Function 接口
[2]. JAVA8 的 java.util.function 包


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  KRe60ogUm4le   2024年04月26日   30   0   0 java算法
  KRe60ogUm4le   2024年05月03日   56   0   0 javascala
0ay4xuMOEQMf
最新推荐 更多

2024-05-31