函數(shù)式編程基礎(chǔ)

前言

何謂函數(shù)式編程

編程語言主要有三種類型:

  1. 命令式編程:專注于“如何去做”呜象,所有的事情都按照你的命令去做捅暴;

  2. 函數(shù)式編程:把運算的過程盡量表現(xiàn)成一系列的嵌套函數(shù)調(diào)用吉捶,相比命令式編程關(guān)心解決問題的步驟,它更關(guān)心數(shù)據(jù)之間的映射關(guān)系角钩,是面向數(shù)學的抽象赚抡;

  3. 邏輯式編程:它通過設定答案必須符合的規(guī)則來解決問題爬坑,過程是事實加上規(guī)則就等于最后的結(jié)果。

函數(shù)式編程并不是java提出的新概念涂臣,在java8之前盾计,我們關(guān)注的都是某一類對象有什么樣的特性(抽象數(shù)據(jù)),當然這也是面向?qū)ο缶幊痰幕A(chǔ)赁遗,在java8出現(xiàn)之后署辉,我們更加關(guān)注行為(抽象行為),這也是java8提出函數(shù)式編程的目的岩四。至于lambda表達式哭尝,其實也就是為了支持函數(shù)式編程的。

函數(shù)式接口

為了支持lambda表達式剖煌,函數(shù)式接口應運而生材鹦。所謂的函數(shù)式接口指的是有且只有一個未實現(xiàn)的方法的接口逝淹,一般可以通過FunctionalInterface這個注解來表明接口是一個函數(shù)式接口。函數(shù)式接口是整個函數(shù)式編程的基礎(chǔ)桶唐。java8之前其實就已經(jīng)有一些函數(shù)式接口了栅葡,比如java.lang.Runnablejava.util.concurrent.Callable尤泽,java.util.Comparator等欣簇,java8更是提出了一些新的函數(shù)式接口:SupplierConsumer坯约,Function醉蚁,Predicate。接下來本文將依次介紹java8提出的常用函數(shù)式接口鬼店,看看它們是怎么使我們的代碼更加簡潔更加優(yōu)雅的网棍。

Supplier

Supplier<T>代表了不接受任何參數(shù)就可以獲取一種類型返回結(jié)果的操作,類似于工廠方法妇智。它的方法定義如下:

T get();

我們來看個簡單的使用示例:

@org.junit.Test
public void testSupplier() {
    Supplier<User> supplier = () -> new User("miaomiao", 24);
    System.out.println(supplier.get());
}

class User {
    /**
     * 用戶名稱
     */
    private String userName;

    /**
     * 用戶年齡
     */
    private int age;

    public User(String userName, int age) {
        this.userName = userName;
        this.age = age;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "userName='" + userName + '\'' +
                ", age=" + age +
                '}';
    }
}

Consumer

Consumer<T>代表了接受一個輸入?yún)?shù)并且無返回的操作滥玷,如果某一類操作不需要返回接口,可以對該類操作抽取邏輯巍棱。它常用的方法定義如下:

void accept(T t);

除了該方法外惑畴,Consumer還提供andThen方法:

default Consumer<T> andThen(Consumer<? super T> after) {
    Objects.requireNonNull(after);
    return (T t) -> { accept(t); after.accept(t); };
}

從方法實現(xiàn)可以看出,在當前Consumer可以指定后續(xù)調(diào)用的其他Consumer航徙。我們來通過一個簡單的例子來看下它是如何使用的:

@org.junit.Test
public void testConsumer() {
    Consumer<String> before = System.out::println;
    Consumer<String> after = n -> System.out.println(n + "_test");
    //執(zhí)行before
    before.accept("consumer");
    //先執(zhí)行before再執(zhí)行after
    before.andThen(after).accept("consumer");
}

運行結(jié)果:

consumer
consumer
consumer_test

Function

Function<T, R>代表了一個函數(shù)如贷,它接受一個輸入?yún)?shù)并且返回相應的一個結(jié)果。其中T代表函數(shù)輸入到踏,R代表函數(shù)輸出杠袱。它主要有以下四個方法:applycompose窝稿,andThen楣富,identity

//將Function對象應用到輸入的參數(shù)上,然后返回計算結(jié)果
R apply(T t);

//返回一個先執(zhí)行before函數(shù)對象的apply方法再執(zhí)行當前函數(shù)對象的apply方法的函數(shù)對象
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
    Objects.requireNonNull(before);
    return (V v) -> apply(before.apply(v));
}

//返回一個先執(zhí)行當前函數(shù)對象的apply方法再執(zhí)行after函數(shù)對象的apply方法的函數(shù)對象
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
    Objects.requireNonNull(after);
    return (T t) -> after.apply(apply(t));
}

//返回一個不經(jīng)過任何處理的函數(shù)對象伴榔,即輸入與輸出相等
static <T> Function<T, T> identity() {
    return t -> t;
}

其中需要注意的是纹蝴,composeandThen的不同之處就在于它們的函數(shù)調(diào)用順序是不一樣的。compose先執(zhí)行參數(shù)函數(shù)再執(zhí)行調(diào)用者踪少,而andThen則先執(zhí)行調(diào)用者再執(zhí)行參數(shù)塘安,這樣看起來可能比較繞,我們來個簡單的例子看一波:

@org.junit.Test
public void testFunction() {
    Function<Integer, Integer> add = n -> (n + 10);
    Function<Integer, Integer> square = n -> (n * n);
    //計算n + 10并返回結(jié)果
    System.out.println("add value = " + add.apply(10));
    //計算n * n并返回結(jié)果
    System.out.println("square value = " + square.apply(10));
    //先計算n * n援奢,以其結(jié)果為參數(shù)計算n + 10
    System.out.println("compose value = " + add.compose(square).apply(10));
    //先計算n + 10兼犯,以其結(jié)果為參數(shù)計算n * n
    System.out.println("andThen value = " + add.andThen(square).apply(10));
    Object identity = Function.identity().apply("miaomiaoLoveCode");
    System.out.println(identity);
}

我們來看下輸出結(jié)果:

add value = 20
square value = 100
compose value = 110
andThen value = 400
miaomiaoLoveCode

Predicate

Predicate<T>代表了接受一個輸入?yún)?shù),返回一個布爾值結(jié)果,主要用于判斷輸入?yún)?shù)是否滿足某種條件免都,滿足條件返回true锉罐,否則返回false。它有以下五個方法:test绕娘,and脓规,negateor险领,isEqual

//判斷輸入?yún)?shù)是否滿足某種條件
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);
}

還是來個例子看一波:

@org.junit.Test
public void testPredicate() {
    Predicate<String> predicate1 = str -> str.equals("miao");
    Predicate<String> predicate2 = str -> str.startsWith("m");
    System.out.println(predicate1.test("miao"));
    System.out.println(predicate2.test("miaomiao"));
    System.out.println(predicate1.and(predicate2).test("miao"));
    System.out.println(predicate1.negate().test("miaomiaoLoveCode"));
    System.out.println(predicate1.or(predicate2).test("miaomiao"));
}

運行結(jié)果:

true
true
true
true
true

到這里為止侨舆,java8的函數(shù)式編程就掃盲完畢,看绢陌,使用函數(shù)式編程后我們的代碼是不是更簡短了挨下,其實代碼還可以更精簡些。ps:后文將會繼續(xù)介紹另外兩個精簡代碼神器:Stream和Optional脐湾,歡迎大家持續(xù)關(guān)注呀~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末臭笆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子秤掌,更是在濱河造成了極大的恐慌愁铺,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闻鉴,死亡現(xiàn)場離奇詭異茵乱,居然都是意外死亡,警方通過查閱死者的電腦和手機孟岛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進店門瓶竭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人渠羞,你說我怎么就攤上這事斤贰。” “怎么了堵未?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵腋舌,是天一觀的道長。 經(jīng)常有香客問我渗蟹,道長,這世上最難降的妖魔是什么赞辩? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任雌芽,我火速辦了婚禮,結(jié)果婚禮上辨嗽,老公的妹妹穿的比我還像新娘世落。我一直安慰自己,他們只是感情好糟需,可當我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布屉佳。 她就那樣靜靜地躺著谷朝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪武花。 梳的紋絲不亂的頭發(fā)上圆凰,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天,我揣著相機與錄音体箕,去河邊找鬼专钉。 笑死,一個胖子當著我的面吹牛累铅,可吹牛的內(nèi)容都是我干的跃须。 我是一名探鬼主播,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼娃兽,長吁一口氣:“原來是場噩夢啊……” “哼菇民!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起投储,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤玉雾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后轻要,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體复旬,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年冲泥,在試婚紗的時候發(fā)現(xiàn)自己被綠了驹碍。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡凡恍,死狀恐怖志秃,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情嚼酝,我是刑警寧澤浮还,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站闽巩,受9級特大地震影響钧舌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜涎跨,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一洼冻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧隅很,春花似錦撞牢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽所宰。三九已至,卻和暖如春畜挥,著一層夾襖步出監(jiān)牢的瞬間仔粥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工砰嘁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留件炉,地道東北人。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓矮湘,卻偏偏與公主長得像斟冕,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子缅阳,可洞房花燭夜當晚...
    茶點故事閱讀 43,562評論 2 349

推薦閱讀更多精彩內(nèi)容