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方法的實現)。