Java 函数式接口

Java 函数式接口

Scroll Down

前言

今天主要讲解 Java 8 新特性 - 函数式接口,虽然现在已经都到 JDK 16了,但相信目前使用 Java 8 的同学应该还是占多数吧。

那究竟什么是函数式接口?

简单概括:有且仅有一个抽象方法,但可以有多个非抽象方法的接口。

Java 8 中新增 @FunctionalInterface 注解,而这个注解主要作用是定义该接口为函数式接口。

定义一个函数式接口 @FunctionalInterface 注解不是必须要添加的,只要该接口符合函数式接口的定义即可,但如果接口不符合函数式接口的定义,但又加上 @FunctionalInterface 注解,编译器会直接报错。

正文

在 Java 8 中新增了很多抽象的函数式接口,比如我们常见的 Supplier、Consumer、Function 等,这些函数式接口都定义在 java.util.function 包下。

下面就几个比较常见的接口简单介绍一下:

Consumer

Consumer,消费型接口,接收传入的泛型参数,执行 accept() 方法。

根据接口的命名和定义的方法可以知道, Consumer 接口主要场景:处理接收的输入参数,类似消费者的角色。

源码定义:

@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); };
    }
}

使用姿势:

public static void main(String[] args) {
	Consumer<String> consumer = (str) -> System.out.println(str);
  // 1. 直接执行accept方法
  consumer.accept("hello world!");
  // 2. 配合stream使用
  Stream.of("1", "2", "3").forEach(consumer);
}

于此类似的还有 BiConsumerIntConsumerDoubleConsumerLongConsumer 等接口,区别的是定义的传参类型不一样或者可以传入多个泛型参数。

Supplier

Supplier,提供型接口,调用 get() 方法返回泛型 T 参数

根据接口的命名和定义的方法可以知道, Supplier 接口主要场景:数据生成,类似提供者的角色。

源码定义:

@FunctionalInterface
public interface Supplier<T> {

    T get();
}

使用姿势:

public static void main(String[] args) {
	Supplier<Integer> supplier = () -> new Random().nextInt();
  System.out.println(supplier.get());
}

于此类似的还有 IntSupplierDoubleSupplierLongSupplierBooleanSupplier 等接口,返回不同的数据类型。

Predicate

Predicate,英文翻译过来是谓语、断言的意思,其实就是一个返回 boolean 的判断型接口。

源码定义:

@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);
    }
}

使用姿势:

public static void main(String[] args) {
		Predicate<Integer> predicate = (i) -> i > 5;
		System.out.println(predicate.test(6));
		System.out.println(predicate.test(1));
}

Function

Function,功能型接口,接收泛型 T 对象,返回泛型 R 对象。

主要用于数据的处理转换,将输入数据处理后,返回特定需要的输出数据。

源码定义:

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;
    }
}

可以看到方法 apply(T t) 是数据转换的核心函数,但同时 Function 也提供了 compose 和 andThen 两个默认方法。

  • compose:先执行参数的 apply,再执行调用者的 apply
  • andThen:先执行调用者,再执行参数,和compose相反

光看文字可能有点懵,直接看下面的例子可能就一目了然

使用姿势:

public static void main(String[] args) {
		Function<Integer, Integer> function = i -> i * 10;
    System.out.println(function.apply(2));

    // 先执行 2*10 再执行 20+1
    Function<Integer, Integer> com = i -> 1 + i;
    System.out.println(com.compose(function).apply(2));

    // 先执行 2+2 再执行 4*10
    Function<Integer, Integer> andThen = i -> 2 + i;
    System.out.println(andThen.andThen(function).apply(2));
}

于此类似的还有 BiFunction、IntFunction、DoubleFunctionLongFunction 等接口,区别的是定义的传参类型不一样或者可以传入多个泛型参数。

总结

java.util.function 包中定义了很多函数式抽象接口,至于它们的用法,其实熟练之后无非就是就这么几种。

在许多的框架源码中,都用到了这一个特性,因为它不仅大大提高我们代码的简洁性,同时也提高了方法的复用性。

普通的改变,将改变普通

我是宅小年,一个在互联网低调前行的小青年

关注公众号「宅小年」,个人博客 📖 edisonz.cn,阅读更多分享文章