Java 函數(shù)式編程 總結(jié)

函數(shù)式編程

概念:

  • 使用代碼以某種方式操縱其他代碼,與傳統(tǒng)的面向過程的編程以及面向?qū)ο蟮木幊逃兴惶粯犹汛:瘮?shù)式編程可以將方法作為參數(shù)叮阅,并使得調(diào)用者動(dòng)態(tài)執(zhí)行行為璧函。這很大地提高了代碼的可擴(kuò)展性和維護(hù)性。讓行為綁定更加靈活勋桶。

  • 其中大部分用到了Lambda表達(dá)式方法引用脱衙。有點(diǎn)類似與C++中的函數(shù)指針或是C#中的Delegate委托類±裕可以將函數(shù)方法作為一個(gè)變量傳遞給某個(gè)通用形式的接收器捐韩。之后用這個(gè)接收器去執(zhí)行回調(diào)操作。

  • 關(guān)于回調(diào)函數(shù)的定義:回調(diào)函數(shù)就是一個(gè)通過函數(shù)指針調(diào)用的函數(shù)鹃锈。如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個(gè)函數(shù)荤胁,當(dāng)這個(gè)指針被用來調(diào)用其所指向的函數(shù)時(shí),我們就說這是回調(diào)函數(shù)屎债〗稣回調(diào)函數(shù)不是由該函數(shù)的實(shí)現(xiàn)方直接調(diào)用,而是在特定的事件或條件發(fā)生時(shí)由另外的一方調(diào)用的盆驹,用于對(duì)該事件或條件進(jìn)行響應(yīng)圆丹。

  • 函數(shù)指針是C++的概念,在Java中可以理解為函數(shù)的引用召娜。

函數(shù)式接口

  • 語法規(guī)則: 有且僅有一個(gè)抽象方法的接口才能被視為函數(shù)式接口(Functional Interface)运褪。

  • 通常用 @FunctionalInterface來進(jìn)行與其它接口進(jìn)行區(qū)分注釋。

@FunctionalInterface 
interface Callable{ void call();}
  • 用函數(shù)式接口定義的變量來接收右側(cè)的方法引用時(shí)玖瘸,右側(cè)的方法引用會(huì)隱式地轉(zhuǎn)化為函數(shù)式接口的變量秸讹。

? 函數(shù)式接口變量 = 方法的引用 ( = 兩側(cè)性質(zhì)其實(shí)不同,在Java的語法下會(huì)進(jìn)行隱式轉(zhuǎn)換)雅倒。

class Person{
    void eat(){ ... };
}

class Test{
    void test(){
        Person p = new Person();
        Callable c = p::eat;
        c.eat();    //調(diào)用時(shí)璃诀,直接通過函數(shù)式接口中唯一的抽象方法來調(diào)用。
    }
}

Lambda表達(dá)式

又叫匿名函數(shù)蔑匣,Lambda表達(dá)式可以作為方法引用通過等號(hào)隱式轉(zhuǎn)化為左側(cè)的函數(shù)式接口變量劣欢。

interface Runnable{ void run();} //定義了一個(gè)只含一個(gè)函數(shù)的接口

class Test{
    void test{     
        Runnable r;  //用接口定義一個(gè)變量
        r = () -> System.out.println("...") ; // 將Lambda表達(dá)式作為函數(shù)引用 賦值給 r
        r.run(); //運(yùn)行
    }
}

Lambda表達(dá)式中會(huì)自行判斷參數(shù)以及返回值類型
例子略。

方法引用

interface Callable{ void call(int i)}

class Person{
    void walk(int step){...}
}

class Test{
    void test{
        Callbale c;
        c = new Person()::walk;  
    }
}

如果是 普通方法 的引用裁良,就用 對(duì)象名::普通方法名凿将。
如果是 靜態(tài)方法 的引用,就用 類名::靜態(tài)方法名价脾。
如果是 構(gòu)造方法 的引用牧抵,就用 類名::new

如果是 動(dòng)態(tài)對(duì)象綁定 (原文是:非綁定接收)的引用, 還可以用 類名::普通方法名 的形式犀变。這個(gè)時(shí)候需要在函數(shù)式接口的抽象方法上必須加一個(gè)參數(shù)妹孙,作為調(diào)用者傳入。

class Person{
    void eat(){ System.out.println("I'm eating"); }
}

interface Activity{
    void doSomething(Person someone); //必須有個(gè)參數(shù)获枝,用來接收調(diào)用者
}

class Test{
    void test(){
        Person person = new Person();
        Activity act = Person::eat;   //這個(gè)Person::eat 實(shí)際上是一個(gè)Lambda表達(dá)式的簡易寫法: 
                                    //                 (someone) -> { someone.eat(); }
        act.doSomething(person); 
    }
}

用這種方式有個(gè)好處就是可以把調(diào)用者行為分開來蠢正,動(dòng)態(tài)調(diào)用的時(shí)候很方便。比如下面這個(gè)例子:

class Person{
    String name;
    Person(String name){ this.name = name;}

    void eat(){ System.out.println(name+" is eating"); }
    void sleep(){ System.out.println(name+" is sleeping"); }
    void walk(){ System.out.println(name+" is walking"); }
    void work(){ System.out.println(name+" is working");}
}

@FunctionalInterface
interface Activity{
    void doSomething(Person someone);
}

public class FPTest1 {
    public static void main(String[] args) {
        Person[] player = {
                new Person("Jimmy"),
                new Person("Fish"),
                new Person("Jack"),
                new Person("Rose")
        };

        Activity[] act = {
                Person::eat,
                Person::sleep,
                Person::walk,
                Person::work,
        };

        Scanner sc = new Scanner(System.in);
        int pi = sc.nextInt();
        int ai = sc.nextInt();

        SomeOneDoSomething( player[pi] , act[ai] ); //通過下標(biāo)索引省店,動(dòng)態(tài)改變"調(diào)用方"對(duì)應(yīng)要做的"行為"嚣崭。
    }

    static void SomeOneDoSomething(Person someone, Activity activity){
        activity.doSomething(someone);  //將調(diào)用方作為參數(shù)傳入
    }
}



常用內(nèi)置接口

java.util.function包中有許多JDK提供的通用函數(shù)式接口。

其中主要為4種:

  • Suppiler : 用于無輸入地產(chǎn)生結(jié)果
  • Consumer : 用于對(duì)輸入進(jìn)行執(zhí)行或使用
  • Predicate : 用于謂語邏輯的判斷
  • Function : 用于對(duì)輸入加工處理后輸出

1. Suppiler接口

首先是 Suppiler 的源碼:
抽象方法: T get() 無參數(shù)萨西,返回T類型

@FunctionalInterface
public interface Supplier<T> {
    T get();   
}

應(yīng)用:

public class Test {
    void test(){
        Supplier<String> func0 = () -> "Hi!" + "Jimmy!";  //直接調(diào)用Supplier<T>
        System.out.println( func0.get() );
    }
}

2. Consumer接口

首先是 Consumer 的源碼:
抽象方法: accept(T t) 參數(shù)類型T有鹿,無返回類型

public interface Consumer<T> {
    void accept(T t);
    default Consumer<T> andThen(Consumer<? super T> after) {...} //還有這個(gè)默認(rèn)方法暫時(shí)這里不展開了
}

應(yīng)用:

public class Test {
    void test(){
        Consumer<String> func1 = s -> System.out.println(s);
        func1.accept("msg1");
    }
}

3. Predicate接口

首先是 Predicate 的源碼:
抽象方法: boolean test(T t) 接收T類型,返回boolean

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
    default Predicate<T> and(Predicate<? super T> other) {...}  //暫略
    default Predicate<T> negate() {...} //暫略
    // ... 還有好多暫略
}

應(yīng)用:

public class Test {
    void test(){
        Predicate<Integer> isZero = num -> num==0? true:false;
        System.out.println(isZero.test(1));
    }
}

4. Function接口

首先是 Function 的源碼:
抽象方法:R apply(T t); 接收T類型谎脯,返回R類型

public interface Function<T, R> {
    R apply(T t);
    //... 其余暫略
}

應(yīng)用:

public class Test {
    void test(){
        Function<String,Integer> str2int = s -> Integer.parseInt(s);
        int a = str2int.apply("10");
        sout(a);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末葱跋,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子源梭,更是在濱河造成了極大的恐慌娱俺,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件废麻,死亡現(xiàn)場離奇詭異荠卷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)烛愧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門油宜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人怜姿,你說我怎么就攤上這事慎冤。” “怎么了沧卢?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵蚁堤,是天一觀的道長。 經(jīng)常有香客問我但狭,道長披诗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任立磁,我火速辦了婚禮呈队,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘唱歧。我一直安慰自己掂咒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布迈喉。 她就那樣靜靜地躺著绍刮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪挨摸。 梳的紋絲不亂的頭發(fā)上孩革,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音得运,去河邊找鬼膝蜈。 笑死,一個(gè)胖子當(dāng)著我的面吹牛熔掺,可吹牛的內(nèi)容都是我干的饱搏。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼置逻,長吁一口氣:“原來是場噩夢啊……” “哼推沸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起券坞,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤鬓催,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后恨锚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宇驾,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年猴伶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了课舍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡他挎,死狀恐怖筝尾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情雇盖,我是刑警寧澤忿等,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站崔挖,受9級(jí)特大地震影響贸街,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜狸相,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一薛匪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧脓鹃,春花似錦逸尖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽岩齿。三九已至,卻和暖如春苞俘,著一層夾襖步出監(jiān)牢的瞬間盹沈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國打工吃谣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留乞封,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓岗憋,卻偏偏與公主長得像肃晚,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子仔戈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355