Java8 添加了一個新的特性Function,顧名思義這必定是一個函數式的操做。咱們知道Java8的最大特性就是函數式接口。全部標註了@FunctionalInterface
註解的接口都是函數式接口,具體來講,全部標註了該註解的接口都將能用在lambda表達式上。java
標註了@FunctionalInterface
的接口有不少,但此篇咱們主要講Function,瞭解了Function其餘的操做也就很容易理解了。編程
@FunctionalInterface public interface Function<T, R> { R apply(T t); /** * @return a composed function that first applies the {@code before} * function and then applies this function */ default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } /** * @return a composed function that first applies this function and then * applies the {@code after} function */ default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } }
爲了方便地閱讀源碼,咱們須要瞭解一些泛型的知識,若是你對泛型已經很熟悉了,那你能夠跳過這段 。app
泛型是JDK1.5引入的特性,經過泛型編程可使編寫的代碼被不少不一樣的類型所共享,這能夠很好的提升代碼的重用性。由於本篇重點不是介紹泛型,因此咱們只關注上述Function源碼須要用到的泛型含義。函數式編程
泛型類使用<T>
來表示該類爲泛型類,其內部成員變量和函數的返回值均可覺得泛型<T>
,Function源碼的標識爲<T,R>
,也就是兩個泛型參數,此處再也不贅述,具體泛型類能夠看網上的文章。函數
在方法修飾符的後面加一個<T>
代表該方法爲泛型方法,如Function 的源碼裏的compose方法的<V>
。通配符也很好理解,仍是compose的例子,咱們能夠看到compose的參數爲一個Function類型,其中Functin的參數指定了其第一個參數必須是V的父類,第二個參數必須繼承T,也就是T的子類。ui
講完了上面這些就能夠開始研究源碼了。this
首先咱們已經知道了Function是一個泛型類,其中定義了兩個泛型參數T和R,在Function中,T表明輸入參數,R表明返回的結果。也許你很好奇,爲何跟別的java源碼不同,Function 的源碼中並無具體的邏輯呢?spa
其實這很容易理解,Function 就是一個函數,其做用相似於數學中函數的定義 ,(x,y)跟<T,R>的做用幾乎一致。
\[ y=f(x) \]
因此Function中沒有具體的操做,具體的操做須要咱們去爲它指定,所以apply具體返回的結果取決於傳入的lambda表達式。code
R apply(T t);
舉個例子:繼承
public void test(){ Function<Integer,Integer> test=i->i+1; test.apply(5); } /** print:6*/
咱們用lambda表達式定義了一個行爲使得i自增1,咱們使用參數5執行apply,最後返回6。這跟咱們之前看待Java的眼光已經不一樣了,在函數式編程以前咱們定義一組操做首先想到的是定義一個方法,而後指定傳入參數,返回咱們須要的結果。函數式編程的思想是先不去考慮具體的行爲,而是先去考慮參數,具體的方法咱們能夠後續再設置。
再舉個例子:
public void test(){ Function<Integer,Integer> test1=i->i+1; Function<Integer,Integer> test2=i->i*i; System.out.println(calculate(test1,5)); System.out.println(calculate(test2,5)); } public static Integer calculate(Function<Integer,Integer> test,Integer number){ return test.apply(number); } /** print:6*/ /** print:25*/
咱們經過傳入不一樣的Function,實現了在同一個方法中實現不一樣的操做。在實際開發中這樣能夠大大減小不少重複的代碼,好比我在實際項目中有個新增用戶的功能,可是用戶分爲VIP和普通用戶,且有兩種不一樣的新增邏輯。那麼此時咱們就能夠先寫兩種不一樣的邏輯。除此以外,這樣還讓邏輯與數據分離開來,咱們能夠實現邏輯的複用。
固然實際開發中的邏輯可能很複雜,好比兩個方法F1,F2都須要兩個個邏輯AB,可是F1須要A->B,F2方法須要B->A。這樣的咱們用剛纔的方法也能夠實現,源碼以下:
public void test(){ Function<Integer,Integer> A=i->i+1; Function<Integer,Integer> B=i->i*i; System.out.println("F1:"+B.apply(A.apply(5))); System.out.println("F2:"+A.apply(B.apply(5))); } /** F1:36 */ /** F2:26 */
也很簡單呢,可是這還不夠複雜,假如咱們F1,F2須要四個邏輯ABCD,那咱們還這樣寫就會變得很麻煩了。
compose和andThen能夠解決咱們的問題。先看compose的源碼
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); }
compose接收一個Function參數,返回時先用傳入的邏輯執行apply,而後使用當前Function的apply。
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); }
andThen跟compose正相反,先執行當前的邏輯,再執行傳入的邏輯。
這樣說可能不夠直觀,我能夠換個說法給你看看
compose等價於B.apply(A.apply(5)),而andThen等價於A.apply(B.apply(5))。
public void test(){ Function<Integer,Integer> A=i->i+1; Function<Integer,Integer> B=i->i*i; System.out.println("F1:"+B.apply(A.apply(5))); System.out.println("F1:"+B.compose(A).apply(5)); System.out.println("F2:"+A.apply(B.apply(5))); System.out.println("F2:"+B.andThen(A).apply(5)); } /** F1:36 */ /** F1:36 */ /** F2:26 */ /** F2:26 */
咱們能夠看到上述兩個方法的返回值都是一個Function,這樣咱們就可使用建造者模式的操做來使用。
B.compose(A).compose(A).andThen(A).apply(5);
這個操做很簡單,你能夠本身試試。