探索Java8:(二)Function接口的使用

Java8 添加了一個(gè)新的特性Function达布,顧名思義這一定是一個(gè)函數(shù)式的操作。我們知道Java8的最大特性就是函數(shù)式接口馅而。所有標(biāo)注了@FunctionalInterface注解的接口都是函數(shù)式接口祥诽,具體來說,所有標(biāo)注了該注解的接口都將能用在lambda表達(dá)式上瓮恭。

標(biāo)注了@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));
    }
}

為了方便地閱讀源碼维哈,我們需要了解一些泛型的知識(shí),如果你對(duì)泛型已經(jīng)很熟悉了登澜,那你可以跳過這段 阔挠。

泛型是JDK1.5引入的特性,通過泛型編程可以使編寫的代碼被很多不同的類型所共享帖渠,這可以很好的提高代碼的重用性谒亦。因?yàn)楸酒攸c(diǎn)不是介紹泛型,所以我們只關(guān)注上述Function源碼需要用到的泛型含義空郊。

1. 泛型類

泛型類使用<T>來表示該類為泛型類,其內(nèi)部成員變量和函數(shù)的返回值都可以為泛型<T> 切揭,F(xiàn)unction源碼的標(biāo)識(shí)為<T,R>狞甚,也就是兩個(gè)泛型參數(shù),此處不再贅述廓旬,具體泛型類可以看網(wǎng)上的文章哼审。

2. 泛型方法和通配符

在方法修飾符的后面加一個(gè)<T>表明該方法為泛型方法,如Function 的源碼里的compose方法的<V>孕豹。通配符也很好理解涩盾,還是compose的例子,我們可以看到compose的參數(shù)為一個(gè)Function類型励背,其中Functin的參數(shù)指定了其第一個(gè)參數(shù)必須是V的父類春霍,第二個(gè)參數(shù)必須繼承T,也就是T的子類叶眉。

源碼解析

1.apply

講完了上面這些就可以開始研究源碼了址儒。

首先我們已經(jīng)知道了Function是一個(gè)泛型類,其中定義了兩個(gè)泛型參數(shù)T和R衅疙,在Function中莲趣,T代表輸入?yún)?shù),R代表返回的結(jié)果饱溢。也許你很好奇喧伞,為什么跟別的java源碼不一樣,F(xiàn)unction 的源碼中并沒有具體的邏輯呢?

其實(shí)這很容易理解潘鲫,F(xiàn)unction 就是一個(gè)函數(shù)绿聘,其作用類似于數(shù)學(xué)中函數(shù)的定義 ,(x,y)跟<T,R>的作用幾乎一致次舌。
y=f(x)
所以Function中沒有具體的操作熄攘,具體的操作需要我們?nèi)樗付ǎ虼薬pply具體返回的結(jié)果取決于傳入的lambda表達(dá)式彼念。

 R apply(T t);

舉個(gè)例子:

public void test(){
    Function<Integer,Integer> test=i->i+1;
    test.apply(5);
}
/** print:6*/

我們用lambda表達(dá)式定義了一個(gè)行為使得i自增1挪圾,我們使用參數(shù)5執(zhí)行apply,最后返回6逐沙。這跟我們以前看待Java的眼光已經(jīng)不同了哲思,在函數(shù)式編程之前我們定義一組操作首先想到的是定義一個(gè)方法,然后指定傳入?yún)?shù)吩案,返回我們需要的結(jié)果棚赔。函數(shù)式編程的思想是先不去考慮具體的行為,而是先去考慮參數(shù)徘郭,具體的方法我們可以后續(xù)再設(shè)置靠益。

再舉個(gè)例子:

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,實(shí)現(xiàn)了在同一個(gè)方法中實(shí)現(xiàn)不同的操作残揉。在實(shí)際開發(fā)中這樣可以大大減少很多重復(fù)的代碼胧后,比如我在實(shí)際項(xiàng)目中有個(gè)新增用戶的功能,但是用戶分為VIP和普通用戶抱环,且有兩種不同的新增邏輯壳快。那么此時(shí)我們就可以先寫兩種不同的邏輯。除此之外镇草,這樣還讓邏輯與數(shù)據(jù)分離開來眶痰,我們可以實(shí)現(xiàn)邏輯的復(fù)用

當(dāng)然實(shí)際開發(fā)中的邏輯可能很復(fù)雜梯啤,比如兩個(gè)方法F1,F2都需要兩個(gè)個(gè)邏輯AB竖伯,但是F1需要A->B,F(xiàn)2方法需要B->A条辟。這樣的我們用剛才的方法也可以實(shí)現(xiàn)黔夭,源碼如下:

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 */

也很簡(jiǎn)單呢,但是這還不夠復(fù)雜羽嫡,假如我們F1,F2需要四個(gè)邏輯ABCD本姥,那我們還這樣寫就會(huì)變得很麻煩了。

2.compose和andThen

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接收一個(gè)Function參數(shù)婚惫,返回時(shí)先用傳入的邏輯執(zhí)行apply氛赐,然后使用當(dāng)前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正相反先舷,先執(zhí)行當(dāng)前的邏輯艰管,再執(zhí)行傳入的邏輯。

這樣說可能不夠直觀蒋川,我可以換個(gè)說法給你看看

compose等價(jià)于B.apply(A.apply(5))牲芋,而andThen等價(jià)于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 */

我們可以看到上述兩個(gè)方法的返回值都是一個(gè)Function捺球,這樣我們就可以使用建造者模式的操作來使用缸浦。

B.compose(A).cpmpose(A).andThen(A).apply(5);

這個(gè)操作很簡(jiǎn)單,你可以自己試試氮兵。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末裂逐,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子泣栈,更是在濱河造成了極大的恐慌卜高,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件南片,死亡現(xiàn)場(chǎng)離奇詭異掺涛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)铃绒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門鸽照,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人颠悬,你說我怎么就攤上這事《ㄑ” “怎么了赔癌?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)澜沟。 經(jīng)常有香客問我灾票,道長(zhǎng),這世上最難降的妖魔是什么茫虽? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任刊苍,我火速辦了婚禮,結(jié)果婚禮上濒析,老公的妹妹穿的比我還像新娘正什。我一直安慰自己,他們只是感情好号杏,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布婴氮。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪主经。 梳的紋絲不亂的頭發(fā)上荣暮,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天,我揣著相機(jī)與錄音罩驻,去河邊找鬼穗酥。 笑死,一個(gè)胖子當(dāng)著我的面吹牛惠遏,可吹牛的內(nèi)容都是我干的砾跃。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼爽哎,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蜓席!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起课锌,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤厨内,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后渺贤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體雏胃,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年志鞍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瞭亮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡固棚,死狀恐怖统翩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情此洲,我是刑警寧澤厂汗,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站呜师,受9級(jí)特大地震影響娶桦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜汁汗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一衷畦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧知牌,春花似錦祈争、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽暖释。三九已至,卻和暖如春墨吓,著一層夾襖步出監(jiān)牢的瞬間球匕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工帖烘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留亮曹,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓秘症,卻偏偏與公主長(zhǎng)得像照卦,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子乡摹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360