Java基础丨函数式接口

前言

函数式接口(Functional Interface)是 Java 8 对一类特殊类型的接口的称呼,点击打开官方文档,就可以看到 java.util.function 包的介绍:“Functional interfaces provide target types for lambda expressions and method references.”以及其中的所有接口的汇总信息。
官方文档

接口分类

粗略观察都不难发现接口的命名都是“数据类型” + “接口类型”的形式,而接口类型可以大致分为以下 4 类:

接口类型说明
Consumer消费型接口接收一个参数,但不返回结果
Function<T, R>函数型接口接收一个参数,返回生成结果
Predicate断言型接口接收一个参数,返回 boolean 值
Supplier供给型接口不接收参数,但返回结果

除此以外,还有一些:

  • BiFunction<T, U, R>:接受两个不同类型的参数(泛型 T 类型和 泛型 U 类型),然后返回一个其他类型的值(泛型 R 类型)。
  • UnaryOperator<T>:继承于 Function<T, T>,接收一个参数(泛型 T 类型),返回与参数相同类型的结果。

    接口使用

    Consumer 接口

    源码如下所示。

    package java.util.function;
    
    import java.util.Objects;
    
    @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); };
      }
    }

    只有两个方法,一个是抽象方法 accept(),传入一个任意类型参数,无返回值,可以用于 Lambda 表达式和方法引用;另一个是默认方法 andThen(),可以传入一个 Consumer,返回组合了两个 Consumer 后的 Consumer ,传入的 Consumer 不能为 null,否则会得到 NullPointerException。

    使用示范

    注意:当调用 andThen() 方法的时候,并不是执行 accept(t); after.accept(t); 这段代码,而是返回了一个 Consumer 接口,这个返回的结果再调用 accept() 方法就执行处理逻辑:当前 Consumer 先执行 accept() 方法,然后再让 after 这个 Consumer 执行 accept() 方法。

    public class ConsumerFunctionTest {
      public static void main(String[] args) {
          // 1.accept() 接收 String 类型
          Consumer<String> lengthConsumer = s -> System.out.println("字符串的长度:" + s.length());
          lengthConsumer.accept("newabug.top");
          // 2.accept() 接收 int 类型
          Consumer<Integer> modifyTheValue = x -> System.out.println("接收的是" + x + ",输出的是" + (x * 2));
          modifyTheValue.accept(10);
          // 3.lengthConsumer 调用 andThen() 返回了一个 Consumer 接口
          Consumer<String> printConsumer = lengthConsumer.andThen(System.out::println);
          // 这个返回的 Consumer 接口再根据顺序执行 accept()
          printConsumer.accept("www.baidu.com");
          // 4.也可以这么写
          Consumer<Integer> changeTheValue = y -> System.out.println("去年是" + y + "年,现在是" + (y + 1));
          changeTheValue.andThen(modifyTheValue).accept(2021);
      }
    }

    更多

    我们使用 Stream.forEach(Consumer<? super T> action) 方法的时候,该方法就是使用了 Consumer 接口作为参数。除了打印信息,一般我们对于集合中的对象的某些数据需要更改,也经常使用 forEach 遍历,然后对于每个对象做一些业务操作。
    除了 Consumer 之外,Java 8 还提供了适用于不同类型的 Consumer,如下表所示。

    接口名称方法描述
    BiConsumer<T, U>void accept(T t, U u)传入两个任意类型参数,无返回值
    DoubleConsumervoid accept(double value)传入一个 double 参数,无返回值
    IntConsumervoid accept(int value)传入一个 int 参数,无返回值
    LongConsumervoid accept(long value)传入一个 long 参数,无返回值
    ObjDoubleConsumer<T>void accept(T t, double value)传入一个任意类型参数,一个 double 参数,无返回值
    ObjIntConsumer<T>void accept(T t, int value)传入一个任意类型参数,一个 int 参数,无返回值
    ObjLongConsumer<T>void accept(T t, long value)传入一个任意类型参数,一个 long 参数,无返回值

    Function 接口

    源码如下所示。

    package java.util.function;
    
    import java.util.Objects;
    
    @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;
      }
    }

    Function 接口包含的单一抽象方法为 apply(T t),它可以将 T 类型的泛型输入参数转换为 R 类型的泛型输出值。
    默认方法有两个,一个是 compose(),另一个是 andThen(),传入的参数都是 Function 类型,都不能为 null,返回的结果都是 Function 类型组合函数,区别是 compose(before) 方法返回一个先执行 before 函数对象 apply() 方法,再执行当前函数对象 apply() 方法的函数对象,而 andThen(after) 方法返回一个先执行当前函数对象 apply() 方法, 再执行 after 函数对象 apply() 方法的函数对象。
    静态方法 identity() 返回一个始终返回其输入参数的函数。

    使用示范

    public class FunctionTest {
      public static void main(String[] args) {
          // 1.使用 R apply(T t),输入 String 类型,得到其长度
          Function<String, Integer> getStrLength = String::length;
          int length = getStrLength.apply("newabug.top");
          System.out.println("字符串的长度:" + length);
          // 2.使用 compose(Function before),返回的也是 Function 的结果
          Function<Integer, Integer> multiplyValue = (value) -> value * 2;
          Function<Integer, Integer> addValue = (value) -> value + 3;
          // addValue 作为 before 参数传入 compose() 方法
          Function<Integer, Integer> addThenMultiply = multiplyValue.compose(addValue);
          Integer result1 = addThenMultiply.apply(1);
          // 根据结果可知,是先执行 (1 + 3) 再执行 * 2
          System.out.println(result1);
          // 3.使用 andThen(Function after)
          Function<Integer, Integer> multiplyThenAdd = multiplyValue.andThen(addValue);
          Integer result2 = multiplyThenAdd.apply(1);
          // 根据结果可知,是先执行 1 * 2 再执行 + 3
          System.out.println(result2);
          // 4.使用 identity(),将 Stream 转换成容器或 Map
          Stream<String> stream = Stream.of("今天的", "风", "甚是喧嚣", "11:22:38");
          Map<String, Integer> map = stream.collect(Collectors.toMap(Function.identity(), String::length));
          for (Integer value : map.values()) {
              System.out.println(value);
          }
      }
    }

    更多

    除了 Function<T,R> 之外,还有各种适用于不同类型的,如下表所示。

    接口名称方法描述
    BiFunction<T, U, R>R apply(T t, U u)传入两个任意类型参数,返回值类型为泛型 R
    DoubleFunction<R>R apply(double value)传入一个 double 参数,返回值类型为泛型 R
    DoubleToIntFunctionint applyAsInt(double value)传入一个 double 参数,返回值类型为 int
    DoubleToLongFunctionlong applyAsLong(double value)传入一个 double 参数,返回值类型为 long
    IntFunction<R>R apply(int value)传入一个 int 参数,返回值类型为泛型 R
    IntToDoubleFunctiondouble applyAsDouble(int value)传入一个 int 参数,返回值类型为 double
    IntToLongFunctionlong applyAsLong(int value)传入一个 int 参数,返回值类型为 long
    LongFunction<R>R apply(long value)传入一个 long 参数,返回值类型为泛型 R
    LongToDoubleFunctiondouble applyAsDouble(long value)传入一个 long 参数,返回值类型为 double
    LongToIntFunctionint applyAsInt(long value)传入一个 long 参数,返回值类型为 int
    ToDoubleBiFunction<T, U>double applyAsDouble(T t, U u)传入参数类型分别为泛型 T, U,返回值类型为 double
    ToDoubleFunction<T>double applyAsDouble(T value)传入参数类型为泛型 T,返回值类型为 double
    ToIntBiFunction<T, U>int applyAsInt(T t, U u)传入参数类型分别为泛型 T, U,返回值类型为 int
    ToIntFunction<T>int applyAsInt(T value)传入参数类型为泛型 T,返回值类型为 int
    ToLongBiFunction<T,U>long applyAsLong(T t, U u)传入参数类型分别为泛型 T, U,返回值类型为 long
    ToLongFunction<T>long applyAsLong(T value)传入参数类型为泛型 T,返回值类型为 long

    Predicate 接口

    源码如下所示。

    package java.util.function;
    
    import java.util.Objects;
    
    @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 接口包含的单一抽象方法为 boolean test(T t),它传入一个泛型参数并返回 true 或 false。
    默认方法有三个,返回值都是 Predicate<T>,分别是 and(Predicate<? super T> other)negate()or(Predicate<? super T> other),使用 and() 方法和 or() 方法都可以让前后两个判断条件一起生效,只不过判断逻辑不同:前者为逻辑与“&&”,存在短路特性,需要所有条件都满足;后者为逻辑或“||”,多个条件只要一个满足即可。negate() 方法会返回一个与指定判断相反的 Predicate,判断逻辑为逻辑非“!”。
    静态方法 isEqual(Object targetRef) 判断两个对象是否相同,先判断对象是否为 null,不为 null 的话再使用 equals() 方法进行比较。

    使用示范

    public class PredicateFunctionTest {
      public static void main(String[] args) {
          // 1.使用 test(T t),返回布尔值
          Predicate<String> strLength = s -> s.length() > 5;
          System.out.println(strLength.test("java"));
          System.out.println(strLength.test("Python"));
          // 2.使用 and(Predicate<? super T> other),拼接判断条件
          Predicate<String> startsWithA = (text) -> text.startsWith("A");
          Predicate<String> endsWithX = (text) -> text.endsWith("s");
          // 条件判断为 &&
          Predicate<String> composed = startsWithA.and(endsWithX);
          String input = "A Boy Can Do Everything For Girls";
          boolean ifSimp = composed.test(input);
          System.out.println("是否舔狗:" + ifSimp);
          // 3.使用 or(Predicate<? super T> other),拼接判断条件
          Predicate<String> composed2 = startsWithA.or(endsWithX);
          // 条件件判断为 ||
          String input2 = "But girls prefer millionaires";
          boolean ifRefuse = composed2.test(input2);
          System.out.println("是否被拒绝:" + ifRefuse);
          // 4.使用 negate()
          List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
          Predicate<Integer> isEven = i -> (i & 1) == 0;
          // 返回一个与指定判断相反的 Predicate
          Predicate<Integer> isOdd = isEven.negate();
          List<Integer> evenNumbers = list.stream().filter(isEven).collect(Collectors.toList());
          List<Integer> oddNumbers = list.stream().filter(isOdd).collect(Collectors.toList());
          System.out.println("偶数:" + evenNumbers);
          System.out.println("奇数:" + oddNumbers);
          // 5.使用 isEqual()
          List<String> programingList = Arrays.asList("Java", "Go", "C++", "Python", "JavaScript", "PHP");
          Predicate<String> isEqual = (n) -> Predicate.isEqual("PHP").test(n);
          programingList.stream().filter(isEqual).forEach((n) -> System.out.println("相等的词语:" + n));
      }
    }

    更多

    Predicate 的 and()or()negate() 方法可以随意组合 Predicate,还有其他一些用法:

  • Optional.filter(Predicate<? super T> predicate):如果值存在且匹配某个给定条件,则返回描述该值的 Optional,否则返回一个空的 Optional。
  • Collection.removeIf(Predicate<? super E> filter):删除集合中所有满足给定条件的元素。
  • Stream.allMatch(Predicate<? super T> predicate):如果流的所有元素均满足给定的条件,则返回 true。

自 Java 11 后,Predicate 接口新增了一个静态方法 not(Predicate<? super T> target),它返回一个 Predicate,该 Predicate 是所提供的 target 的否定。

static <T> Predicate<T> not(Predicate<? super T> target) {
    Objects.requireNonNull(target);
    return (Predicate<T>)target.negate();
}

Predicate 其他类型的接口如下表所示。

接口名称方法描述
BiPredicate<T,U>boolean test(T t, U u)传入参数类型分别为泛型 T, U,返回值类型为 boolean
DoublePredicateboolean test(double value)传入一个 double 参数,返回值类型为 boolean
IntPredicateboolean test(int value)传入一个 int 参数,返回值类型为 boolean
LongPredicateboolean test(long value)传入一个 long 参数,返回值类型为 boolean

Supplier 接口

源码如下所示。

package java.util.function;

@FunctionalInterface
public interface Supplier<T> {

    T get();
}

是的,Supplier 接口相当简单,它不包含任何静态或默认方法,只有一个抽象方法 T get()

使用示范

Supplier 的一种简单应用是 Math.random() 方法,它不传入参数且返回 double 型随机数。

public class SupplierFunctionTest {
    public static void main(String[] args) {
        // 1.返回随机数
        Supplier<Double> randomSupplier = Math::random;
        System.out.println("生成的随机数:" + randomSupplier.get());
        // 2.返回当前时间
        Supplier<LocalDateTime> timeSupplier = LocalDateTime::now;
        System.out.println("当前的时间:" + timeSupplier.get());
    }
}

更多

Java 8 还提供了指定类型的 Supplier,有 BooleanSupplier, DoubleSupplier, IntSupplier, LongSupplier,它们也只有一个获取对应类型返回值的方法 getAsxxx()

参考

菜鸟教程
云+社区 - chenchenchen
打赏
评论区
头像
文章目录