天天看點

【JAVA8】快速了解Consumer、Supplier、Predicate與Function

                          快速了解Consumer、Supplier、Predicate與Function

一、前言

這幾個接口都處在java.util.function包下,Consumer(消費型),Supplier(供給型)、Predicate(判斷型)與Function(轉換型),暫時不了解他們的類型沒關系。

如果對Lambda不怎麼了解的同學,可以先移步到我的另外一篇文章對Lambda的了解

二、Consumer

Consumer是一個消費型的接口,它接收一個🍉,然後對這個西瓜進行消費,連西瓜籽都不帶留下的。

先看Consumer接口的源碼,有一個未實作的抽象方法,和一個預設方法(jdk1.8之後,接口裡面可以有預設方法和靜态方法)。

@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方法,接收一個泛型參數,不傳回任何值。ok,我們來簡單實作它

Consumer<Integer> consumer=new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        };

        consumer.accept(1);
    }      

好了,用腳指頭想着,肯定是輸出1了。

接下來我們使用lambda表達式來對此匿名内部類進行改寫。此時該lambda的類型就是Consumer類型。

consumer=i-> System.out.println(i);      

當然我們也可以使用方法引用

consumer=System.out::println;      

在Stream類中,我們發現常用的forEach接口接收一個Consumer類型的參數,源碼如下

void forEach(Consumer<? super T> action);      

二話不說,我們将consumer傳入forEach中,來實作周遊集合的操作。

List<Integer> list= Arrays.asList(1,2,3,4,5);
        Consumer<Integer> consumer= System.out::println;
        list.stream().forEach(consumer);      

将中間consumer對象去掉呢,代碼會變得更加簡潔。咦,到這裡,是不是有一種似曾相識的感覺,原來是這樣演變來的。

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
        list.stream().forEach(System.out::println);      

Consumer總結:

  • Consumer接口是一個消費型的接口,隻要實作它的accept方法,就能作為消費者來輸出資訊。
  • lambda、方法引用都可以是一個Consumer類型,是以他們可以作為forEach的參數,用來協助Stream輸出資訊。
  • Consumer還有很多變種,例如IntConsumer、DoubleConsumer與LongConsumer等,歸根結底,這些變種其實隻是指定了Consumer中的泛型而已,方法上并無變化。

三、Supplier

Supplier是一個供給型的接口,我們可以無條件的從它這裡擷取東西。

@FunctionalInterface
public interface Supplier<T> {

    T get();
}      

我們不需要為get方法傳入任何參數,就能獲得一個結果,這不是白嫖嗎?那我想要一個随機數

Supplier<Double> supplier=()->new Random().nextDouble();
        //當然也可以使用方法引用
        Supplier<Double> supplier1= Math::random;
        System.out.println(supplier.get());      

下一步,Supplier可以哪些地方呢,畢竟是可以白嫖的,誰不喜歡呢?我們看看Supplier在Optional中的應用。

public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }      

該方法接收Supplier類型的參數,當Optional内部的value不為空時,才會傳回Supplier中的值。例如

Optional<Double> optional=Optional.empty();
        Supplier<Double> supplier=()->new Random().nextDouble();
        optional.orElseGet(supplier);      

這必定傳回Supplier中的随機值,因為Optional.empty()包含的值就是null。

Supplier總結:

  • Supplier是一個供給型的接口,其中的get方法用于傳回一個值。
  • Supplier也有很多的變種,例如IntSupplier、LongSupplier與BooleanSupplier等

四、Predicate

Predicate是一個判斷型接口,看看它的源碼。

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

該接口将jdk1.8中接口的變化展現的淋漓盡緻,接口不再“純粹”了,可以有預設方法與靜态方法了,下次面試再問道,就得分情況喽,哭出聲。

要了解一個接口,我們就去實作它的方法。

Predicate<Integer> predicate=i->i>5;
        System.out.println(predicate.test(1));      

很明顯,輸出是false。等等,既然可以進行判斷,那和Stream.filter()有沒有關系呢?

Stream<T> filter(Predicate<? super T> predicate);      

果然是有關系的,啧啧啧,我這敏銳的嗅覺。那我們把Predicate對象傳入filter試試?

List<Integer> list= Arrays.asList(1,2,3,4,5,6,7,8);
        list.stream().filter(i->i>5).forEach(System.out::print);      

很簡單,輸出是678。

Predicate總結:

  • Predicate是一個判斷型的接口,用一個test方法去測試傳入的參數。
  • 當然,Predicate也有對應的變種。

五、Function

Function是一個功能型的接口,用于将一種類型的資料轉化為另外一種類型的資料。

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

重點關注它的apply方法,現在就去實作它,并将之傳入進Stream.map()方法中試試。

public class TestFunction {
    static class Student{
        String name;
        Integer id;

        public Student(String name, Integer id) {
            this.name = name;
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public Integer getId() {
            return id;
        }
    }
    public static void main(String[] args) {
        List<Student> list= Arrays.asList(new Student("jack",1),new Student("tom",2));
        Function<Student,Integer> function= Student::getId;
        list.stream().map(function).forEach(System.out::print);
    }
    
}      

輸出12,可以看得出,Function中的apply方法将Student類型的資料轉化為對應id的Integer類型的資料。

  • Function是一個轉換型的接口,其中的apply可以将一種類型的資料轉化成另外一種類型的資料。
  • Function的變種就更多了。

六、總結

繼續閱讀