1. 程式人生 > >Function介面、BiFunction介面、Consumer介面分析

Function介面、BiFunction介面、Consumer介面分析

Function函式式介面

關於什麼是Function,這裡就不做介紹了。我們就需要知道Function是jdk8提供的一個預設介面。從Function名字,我們就可以看出來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;
    }
}

通過原始碼我們發現,Function是一個函式式介面,裡面只有一個抽象方法apply() 該方法也是申明該函式具體執行什麼樣的操作,同時Function介面也給我們提供了兩個預設方法和一個靜態方法。

用Function能給我們幹什麼?

下面就通過一個簡單的例子,來演示怎麼使用Function介面為我們做事情。

例子:我們需要將輸入一個數據,並給該資料進行一系列的操作輸出。

public class Demo2 {
    public static void main(String[] args) {
        //執行平方操作
        System.out.println(method1(2,(Integer a) -> a*a));
        //將輸入引數都加10
        System.out.println(method1(2,a->a+10));
    }

    public static Integer method1(Integer a,Function<Integer,Integer> function){
        return function.apply(a);
    }
}

我們仔細分析上面的程式碼:可以發現,我們就只需要定義一個方法,而該方法就只是呼叫了Function的apply方法,但是並沒有具體在方法定義的時候給出Function介面中的apply方法的具體實現,而是在方法呼叫的時候對方法進行實現,從而可以定義一個方法為我們做出許多不同的事情,這就是函式式介面也就是函式程式設計的一大優點,可以在方法中傳遞行為或者返回行為

上面已經對apply方法進行了介紹,那麼接下來我們就介紹下兩個預設方法的作用

Function中的compose方法

default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
}

通過這個方法,我們可以發現,該方法接收一個Function型別的引數,並返回一個Function型別的結果。

我們來仔細的研讀這句程式碼:(V v) -> apply(before.apply(v))。對於該句程式碼的執行過程:

  • 首先呼叫before的apply方法,並傳遞一個初始引數
  • 然後再執行該介面中的apply方法,並把before.apply執行的結果作為輸入引數
  • 通過lambda表示式例項化一個新的Function型別並實現apply方法,方法的實現就是返回apply(before.apply(v))的結果

由於不是特別好理解,就舉一個例子:

public class Demo2 {
    public static void main(String[] args) {
        Function<Integer,Integer> function1 = value -> value*3;
        Function<Integer,Integer> function2 = value -> value*value;
        System.out.println(method1(2,function1,function2));
    }

    public static Integer method1(int a, Function<Integer,Integer> function1, Function<Integer,Integer> function2){
        return function1.compose(function2).apply(a);
    }
}
//output:12

通過這段程式碼,我們可以發現該程式碼輸出的結果為12,那麼為什麼是12呢?我們就來仔細的研讀:

  • 按上面說的分析,首先先執行function2.apply(2),
而關於function2的apply方法以及被實現為:

apply(value){
    return value * value;
}
  • 再function1中的apply方法,並把function1.apply(2)的值傳入給function1的apply方法
而關於function1的apply方法以及被實現為:

apply(value){
    return value * 3;
}
  • 然後再通過lambda表示式把計算的結果作為一個新Function型別的例項來實現apply方法
新的Function型別例項的apply方法:
apply(value){
    return function1.apply計算的最終結果
}

Function中的andThen方法

通過原始碼分析,andThen方法和compose方法相反:

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
}
  • 1.先執行呼叫andThen的Function型別的apply方法
  • 2.再執行andThen接收的Function型別引數型別的apply方法,並把第一步計算結果作為輸入
  • 3.生成新的Function型別,並且新Function型別的apply方法的實現是直接返回第二步計算的過的值。

BiFunction函式介面

該函式介面就是接收兩個引數,最終輸出操作後的結果

@FunctionalInterface
public interface BiFunction<T, U, R> {

    R apply(T t, U u);

    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }
}

通過原始碼發現,該介面只有一個抽象方法apply是接收兩個引數,輸出一個值。還有一個預設方法就是andThen方法。仔細觀察,為什麼沒有和上面Function介面中的compose方法呢?

通過上面已經解釋了,andThen方法是先執行呼叫者的apply方法,再執行接受者引數的apply方法。而compose方法是先執行引數的apply方法,後執行呼叫者的apply方法。由於BiFunction是接收兩個引數,只有先執行呼叫者的apply方法才能接收兩個引數。而如果是直接先執行Function型別引數的apply方法是不能接收兩個引數的。因此,在BiFunction方法中就只有一個andThen方法的。

Consumer介面

Consumer通過名字一樣可以知道為消費者。也就是說該介面的主要功能就是傳入資料,而不輸出任何資料處理。

@FunctionalInterface
public interface Consumer<T> {
    void accept(T var1);

    default Consumer<T> andThen(Consumer<? super T> var1) {
        Objects.requireNonNull(var1);
        return (var2) -> {
            this.accept(var2);
            var1.accept(var2);
        };
    }
}

通過上面的原始碼分析,就可以發現:Consumer有一個抽象方法就是accept(該方法就是讓使用者來給定該消費者如何消費傳入的資料),還有一個andThen方法。

Consumer的andThen方法:

default Consumer<T> andThen(Consumer<? super T> var1) {
        Objects.requireNonNull(var1);
        return (var2) -> {
            this.accept(var2);
            var1.accept(var2);
        };
}

通過該原始碼分析:andThen接收一個Consumer型別的引數。關於該方法執行的原理分析:

  • 1.首先先執行呼叫者中的accept方法
  • 2.然後在執行Consumer引數型別的accept方法
  • 3.建立一個新的Consumer型別,並且accept方法中的實現是:把1 2 步的操作合起來(也就是把1和2的操作作為新Consumer型別的accept方法的實現)。