使用 java.util.Function 包中包含的内置接口,如 Predicate、 Consumer、 Function 和 Supplier。开发使用基本功能接口版本的代码。开发使用函数接口的二进制版本的代码。开发使用 UnaryOperator 接口的代码。

为什么要内置Lambda接口

Lambda 表达式必须对应于一个函数接口。
可以使用任何接口作为 lambda 表达式,只要接口只包含一个抽象方法。

内置函数式接口分类

image.png

大体划分类型

  • Predicate
  • Consumer
  • Function<T, R>
  • Supplier
  • UnaryOperator
    t 和 r 表示泛型类型(t 表示参数类型,r 表示返回类型)。

基本类型

对于入参为基本类型的情况,会衍生出其他接口一一对应(只适用于 int、 long、 double 和 boolean),但其没有子父类关系,完全独立
image.png

二进制类型

image.png

Predicate

  • 定义
@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
    // Other default and static methods
    // ...
}

根据其变量的值,该语句可以为 true 或 false。这个函数接口可以用于任何需要计算布尔条件的地方。

  • 函数描述符(方法签名)
T -> boolean
  • 举例
    1.使用匿名类
Predicate<String> startsWithA = new Predicate<String>() {
    @Override
    public boolean test(String t) {
        return t.startsWith("A");
    }
};
boolean result = startsWithA.test("Arthur");

2.使用 lambda 表达式

Predicate<String> startsWithA = t -> t.startsWith("A");
boolean result = startsWithA.test("Arthur");

默认default方法

default Predicate<T> and(Predicate<? super T> other)
default Predicate<T> or(Predicate<? super T> other)
default Predicate<T> negate()

返回Predicate,表示该谓词和另一个谓词的短路逻辑&&和||及其逻辑取反
这些方法有助于组合谓词,使代码更具可读性,

  • 使用
        Predicate<String> startsWithA = t -> t.startsWith("H");
        Predicate<String> endsWithA = t -> t.endsWith("i");
        boolean result = startsWithA.and(endsWithA).test("Hi");
        boolean result1 = startsWithA.or(endsWithA).test("Ai");
        System.out.println(result);
        System.out.println(result1);

image.png

关于基本类型的Predicate

int、 long 和 double 的基本版本,它们不是从 Predicate 扩展而来的

  • 因为范型的自动装箱和拆箱,所以
Predicate<Integer> even = t -> t % 2 == 1;
boolean result = even.test(5);

虽然可以使用,由于从包装类型(Integer)到原始类型(int)的转换使用更多的内存,并伴随着性能开销,所以 Java 提供这些版本以避免在输入或输出是原始类型时进行自动装箱操作。

IntPredicate even = t -> t % 2 == 1;
boolean result = even.test(5);

可以这样使用基本类型

Consumer

  • 定义
@FunctionalInterface
public interface Consumer<T> {
     void accept(T t);
     // And a default method
     // ...
}

受单个输入参数并且不返回结果的操作; 它只对参数执行一些操作

  • 函数描述符(方法签名)
T -> void
  • 举例
    1.使用匿名类
Consumer<String> consumeStr = new Consumer<String>() {
     @Override
     public void accept(String t) {
         System.out.println(t);
     }
};
consumeStr.accept("Hi");

2.使用 lambda 表达式

Consumer<String> consumeStr = t -> System.out.println(t);
consumeStr.accept("Hi");

默认default方法

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

此方法返回一个组合使用者,该使用者按顺序执行使用者的操作,然后执行参数的操作。

  • 使用
Consumer<String> first = t ->
         System.out.println("First:" + t);
Consumer<String> second = t ->
         System.out.println("Second:" + t);
first.andThen(second).accept("Hi");

image.png

关于基本类型的Consumer

和Predicate接口类似不再赘述

Function

  • 定义
@FunctionalInterface
public interface Function<T, R> {
     R apply(T t);
     // Other default and static methods
     // ...
}

接受某种类型的输入参数并生成另一种类型的结果的操作

  • 函数描述符(方法签名)
T -> R
  • 使用
void round(double d, Function<Double, Long> f) {
     long result = f.apply(d);
     System.out.println(result);
}

1.匿名类

round(5.4, new Function<Double, Long>() {
     Long apply(Double d) {
         return Math.round(d);
     }
});

2.使用 lambda 表达式

round(5.4, d -> Math.round(d));

image.png
此时使用lambda表达式就很清晰明了

默认default方法

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

方法的区别在于:
compose 首先调用参数表示的Function,其结果作为应用Function的输入。
andThen 首先应用调用方法的Function,其结果作为由参数表示的Function的输入
image.png
即:

  • compose 先计算后面的入参函数,再作为参数计算应用的函数
    f1是应用的最后一个函数
  • andThen 先计算应用函数,将其作为参数计算后面的函数
    f2是应用的最后一个函数

静态static方法

static <T> Function<T, T> identity()

返回一个总是返回其输入参数的函数。

  • 举例
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("A", 10));
        personList.add(new Person("B", 10));
        personList.add(new Person("C", 10));
        personList.add(new Person("D", 10));
        Map<String, Person> personMap = personList.stream()
                .collect(Collectors.toMap(Person::getName, Function.identity()));
        Map<String, Person> personMap1 = personList.stream()
                .collect(Collectors.toMap(Person::getName,v->v));
        System.out.println(personMap);
        System.out.println(personMap1);

image.png
使用的时候只能再方法的入参处使用,因为其lambda表达式返回其自身

关于基本类型的Function

也适用于 int、 long 和 double,但是比之前的接口有更多的组合
image.png
这些接口是为了方便,可以直接使用原接口

  • DoubleFunction 等价于 Function<Double, R>
  • ToLongFunction 等价于 Function<T, Long>
  • IntToLongFunction 等价于 Function<Integer, Long>

Supplier

  • 定义
@FunctionalInterface
public interface Supplier<T> {
     T get();
}

与Consumer相反,其入参为空,返回泛型T的对象

  • 函数描述符(方法签名)
() -> T
  • 举例
    1.使用匿名类
String t = "One";
Supplier<String> supplierStr = new Supplier<String>() {
     @Override
     public String get() {
         return t.toUpperCase();
     }
};
System.out.println(supplierStr.get());

2.使用lambda表达式

String t = "One";
Supplier<String> supplierStr = () -> t.toUpperCase();
System.out.println(supplierStr.get());

image.png

  • 该接口没有默认default和静态static方法

关于基本类型的Supplier

和其他接口类似,不再赘述

UnaryOperator

  • 定义
@FunctionalInterface
public interface UnaryOperator<T>
         extends Function<T, T> {
     // Just the identity
     // method is defined
}

继承自Function接口,是Function的特殊化用于在参数和结果属于相同类型时使用。
image.png
image.png

  • 函数描述符(方法签名)
T -> T
  • 使用
    1.使用匿名类
UnaryOperator<String> uOp = new UnaryOperator<String>() {
     @Override
public String apply(String t) {
         return t.substring(0,2);
     }
};
System.out.println(uOp.apply("Hello"));

2.使用lambda表达式

UnaryOperator<String> uOp = t -> t.substring(0,2);
System.out.println(uOp.apply("Hello"));

image.png

继承了 Function 接口的默认方法:

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

静态static方法

因为static无法继承,所以重新定义

static <T> UnaryOperator<T> identity()

关于基本类型的IntUnaryOperator

与Function类似

BiPredicate

  • 定义
@FunctionalInterface public interface BiPredicate<T, U> {
     boolean test(T t, U u);
     // Default methods are defined also
}

带有两个参数

  • 函数描述符(方法签名)
(T, U) -> boolean
  • 举例
    1.使用匿名类
BiPredicate<Integer, Integer> divisible =
         new BiPredicate<Integer, Integer>() {
     @Override
     public boolean test(Integer t, Integer u) {
         return t % u == 0;
     }
};
boolean result = divisible.test(10, 5);

2.使用lambda表达式

BiPredicate<Integer, Integer> divisible =
        (t, u) -> t % u == 0;
boolean result = divisible.test(10, 5);

image.png

BiConsumer

表示接受两个参数,且不返回结果

BiFunction

  • 定义
@FunctionalInterface
public interface BiFunction<T, U, R> {
     R apply(T t, U u); // Other default and static methods
     // ...
}

此接口表示接受两个不同类型的参数并生成另一个类型的结果的函数。

  • 函数描述符(方法签名)
(T, U) -> R

BinaryOperator

  • 定义
@FunctionalInterface
public interface BinaryOperator<T>
             extends BiFunction<T,T,T> {
     // Two static method are defined
}

用于在参数和结果属于相同类型时使用

  • 函数描述符(方法签名)
(T, T) -> T

静态static方法

  • 定义了新的静态方法
static <T> BinaryOperator<T> minBy(
                  Comparator<? super T> comparator)
static <T> BinaryOperator<T> maxBy(
                  Comparator<? super T> comparator)

总结

  • Java8包含新的函数接口,用于处理lambda表达式,这些表达式涵盖Java.util.function包中最常见的场景用法。
    Predicate
    Consumer
    Function
    Supplier
    UnaryOperator
  • 这些接口含有int、long和double以及boolean(仅适用于Supplier)的版本,以避免将包装类转换为基本类型(例如,Integer到int)的成本
  • Predicate可以在任何需要计算布尔条件的地方使用。其函数描述符(方法签名)为:
    T->boolean
  • Consumer接受单个输入参数而不返回结果的操作。其功能描述如下:
    T->void
  • Function是接受某一类型的输入参数并产生另一类型结果的操作。其功能描述如下:
    T->R
  • Supplier是一个不带参数的操作,但它返回一些值。其功能描述如下:
    ()->T
  • UnaryOperator是Function接口的一种特殊化(接口是从它扩展而来的),用于参数和结果是同一类型的情况。所以它的功能描述是:
    T->T

这个家伙很懒,啥也没有留下😋