笨读JDK8源码(3)-Consumer和Supplier

2019/09/18

Consumer

源码翻译

/**
 * 表示一个接收单个输入参数且不返回任何结果的操作。不像大部分函数式接口,
 * {@code Consumer}预计通过副作用(side-effects)来实现方法功能。
 *
 * <p>这是一个函数式方法为{@link #accept(Object)}的函数式接口。
 *
 * @param <T> 操作的输入参数类型
 * @author jdk
 * @since 1.8
 */
@FunctionalInterface
public interface Consumer<T> {

    /**
     * 对给定参数执行操作。
     *
     * @param t 输入参数
     */
    void accept(T t);

    /**
     * 返回一个先执行当前Consumer再执行后续Consumer的组合Consumer。
     * 任一Consumer执行过程当中引发的异常由调用者处理。若当前Consumer
     * 抛出了异常,则后续Consumer不执行。
     *
     * @param after 执行当前操作后执行的后续操作
     * @return 一个先执行当前操作再执行后续操作的{@code Consumer}
     * @throws NullPointerException 若后续Consumer为null则抛出
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        // 对后续Consumer判空
        Objects.requireNonNull(after);
        return (T t) -> {
            // 先对输入参数执行当前操作
            accept(t);
            // 再对处理后的输入执行后续操作
            after.accept(t);
        };
    }
}

详解

Consumer 的字面意思是消费者,这个接口用于对单个对象执行不返回结果的操作。该接口提供了一个用于创建组合函数的默认方法。

Consumer 最常用于调用 Iterable 实例的 void forEach(Consumer<? super T> action) 方法,也就是用于替代无返回值的 for-each 循环的循环体。实际上,Consumer 可以用于替代各种只有一个输入参数的 void 方法。

使用示例:

public class Main {

    static class Student {
        int age;

        public Student(int age) {
            this.age = age;
        }

        @Override
        public String toString() {
            return "Student{" +
                    "age=" + age +
                    '}';
        }
    }

    public static void main(String[] args) {
        // 创建列表
        List<Student> list = Arrays.asList(
                new Student(0),
                new Student(1),
                new Student(2),
                new Student(3)
        );
        // 用Lambda表达式创建Consumer
        Consumer<Student> plusOne = student -> {
            student.age += 1;
        };
        // 用方法引用创建Consumer
        Consumer<Student> println = System.out::println;
        // 通过组合Consumer迭代列表
        list.forEach(plusOne.andThen(println));
    }
}

输出结果:

Student{age=1}
Student{age=2}
Student{age=3}
Student{age=4}

在上面这个例子中,第一个 Consumer 对学生的年龄进行了加一操作,而第二个 Consumer 则把学生实例打印出来。通过 forEach 方法传入组合的 Consumer,就实现了迭代的组合操作。

Supplier

源码翻译

/**
 * 表示一个结果提供者。
 *
 * <p>对于supplier的每次执行,不要求提供一个新的或与上次不同的结果。
 *
 * <p>这是一个函数式方法为{@link #get()}的函数式接口。
 *
 * @param <T> 结果类型
 * @author jdk
 * @since 1.8
 */
@FunctionalInterface
public interface Supplier<T> {

    /**
     * 获取一个结果。
     *
     * @return 一个结果
     */
    T get();
}

详解

Supplier 接口实际上实现了从行为的转换。Java 的函数式编程实质上是把行为看作一种对象(函数式接口实例),函数对象封装的是行为而不是值。通过 Supplier 接口,我们就能以返回行为的方式来满足返回值的需求。

Supplier 接口可以用于替代无参工厂方法。

总结

Consumer 接口和 Supplier 接口都是 JDK8 中很重要而又很简单的接口,其源码阅读几乎不费吹灰之力,使用方法也是一看就懂。实际上,只要看懂了 Function 接口和 @FunctionalInterface 注解,java.util.function 这个包下的所有接口都是很好懂的。

JDK8 通过这一系列接口,用面向对象的方式将函数式编程结合到 Java 中,实现了向后兼容。


(转载本站文章请注明作者和出处)

Show Disqus Comments

Post Directory