Java8-函數(shù)式編程

Java8-函數(shù)式編程

為什么java8 的Lambda 這么受關(guān)注制妄?

Java8可以用Lambda表達(dá)式很簡便的寫出復(fù)雜的處理集合的邏輯缨恒。Lambda 可以理解為一種匿名函數(shù)的代替蚕钦。使用它可以簡化代碼,提高開發(fā)效率。

函數(shù)編程演化歷程

函數(shù)編程演化歷程

  • 1.將業(yè)務(wù)邏輯直接寫死在代碼里。
  • 2.將單一維度的條件做為參數(shù)傳入方法中灵嫌。方法內(nèi)根據(jù)參數(shù)進(jìn)行業(yè)務(wù)邏輯實(shí)現(xiàn)。
  • 3.將多個維度的條件做為參數(shù)傳入方法中冀偶。業(yè)務(wù)實(shí)現(xiàn)需要根據(jù)不同的參數(shù)處理不同邏輯醒第。
  • 4.將業(yè)務(wù)邏輯封裝為一個實(shí)體類,方法接受實(shí)體類為參數(shù)进鸠,方法內(nèi)部調(diào)用實(shí)體類的處理邏輯稠曼。
  • 5.調(diào)用方法時不在創(chuàng)建實(shí)體類,而是使用匿名函數(shù)的形式替代客年。
  • 6.使用Lambda表達(dá)式替代匿名函數(shù)的形式霞幅,做為方法的參數(shù)。真正實(shí)現(xiàn)判斷邏輯參數(shù)化傳遞量瓜。

函數(shù)編程

函數(shù)式編程是一種編程范式司恳,我們常見的編程范式有命令式編程(Imperative programming),函數(shù)式編程绍傲,邏輯式編程扔傅,常見的面向?qū)ο缶幊淌且彩且环N命令式編程。

命令式編程是面向計算機(jī)硬件的抽象烫饼,有變量(對應(yīng)著存儲單元)猎塞,賦值語句(獲取,存儲指令)杠纵,表達(dá)式(內(nèi)存引用和算術(shù)運(yùn)算)和控制語句(跳轉(zhuǎn)指令)荠耽,一句話,命令式程序就是一個馮諾依曼機(jī)的指令序列比藻。而函數(shù)式編程是面向數(shù)學(xué)的抽象铝量,將計算描述為一種表達(dá)式求值倘屹,一句話,函數(shù)式程序就是一個表達(dá)式慢叨。

函數(shù)式編程中的函數(shù)這個術(shù)語不是指計算機(jī)中的函數(shù)(實(shí)際上是Subroutine)纽匙,而是指數(shù)學(xué)中的函數(shù),即自變量的映射插爹。也就是說一個函數(shù)的值僅決定于函數(shù)參數(shù)的值哄辣,不依賴其他狀態(tài)。比如sqrt(x)函數(shù)計算x的平方根赠尾,只要x不變力穗,不論什么時候調(diào)用,調(diào)用幾次气嫁,值都是不變的当窗。在函數(shù)式語言中,函數(shù)作為一等公民寸宵,可以在任何地方定義崖面,在函數(shù)內(nèi)或函數(shù)外,可以作為函數(shù)的參數(shù)和返回值梯影,可以對函數(shù)進(jìn)行組合巫员。

Lambda表達(dá)式

Lambda表達(dá)式的兩種形式

  • (parameters) -> expression

  • (parameters) ->{ statements; }
    以下是lambda表達(dá)式的重要特征:

  • 可選類型聲明:不需要聲明參數(shù)類型月匣,編譯器可以統(tǒng)一識別參數(shù)值啥纸。

  • 可選的參數(shù)圓括號:一個參數(shù)無需定義圓括號,但多個參數(shù)需要定義圓括號忿危。

  • 可選的大括號:如果主體包含了一個語句感猛,就不需要使用大括號七扰。

  • 可選的返回關(guān)鍵字:如果主體只有一個表達(dá)式返回值則編譯器會自動返回值,大括號需要指定明表達(dá)式返回了一個數(shù)值陪白。

表達(dá)式實(shí)例

// 1. 不需要參數(shù)
() -> 5 ;
() -> System.out.println("hello world");
  
// 2. 接收一個參數(shù)
x -> 2 * x ;
() -> System.out.println("hello world");

// 3. 沒有參數(shù)邏輯復(fù)雜
() -> {
    System.out.println("hello world1");
    System.out.println("hello world2");
}
// 3. 接受2個參數(shù)(數(shù)字)
BinaryOperator<Long> functionAdd = (x,y)-> x + y ;
Long result = functionAdd(1L,2L);
  
// 4. 接收2個int型整數(shù),顯示聲明參數(shù)類型
(int x, int y) -> x + y  
  
// 5. 接受一個 string 對象,并在控制臺打印,不返回任何值(看起來像是返回void)  
(String s) -> System.out.print(s)

以上就是lambda的介紹颈走。

函數(shù)式接口介紹

通常Lambda表達(dá)式是用在函數(shù)式接口上使用的。從Java8開始引入了函數(shù)式接口咱士,其說明比較簡單:函數(shù)式接口(Functional Interface)就是一個有且僅有一個抽象方法立由,但是可以有多個非抽象方法的接口。 java8引入@FunctionalInterface 注解聲明該接口是一個函數(shù)式接口序厉。

語法定義

@FunctionalInterface
public interface ICollectionService {
    void test();
}

常用的函數(shù)式接口

接口 參數(shù) 返回類型 描述
Predicate<T> T boolean 用于判別一個對象
Consumer<T> T void 用于接收一個對象進(jìn)行處理但沒有返回
Function<T,R> T R 轉(zhuǎn)換一個對象為不同類型的對象
Supplier<T> None T 提供一個對象
UnaryOperator<T> T T 接收對象并返回同類型的對象
BinaryOperator<T> (T,T) T 接收兩個同類型的對象锐膜,并返回一個原類型的對象

Predicate

java.util.function.Predicate<T> 接口定義了一個名叫 test 的抽象方法,它接受泛型 T 對象脂矫,并返回一個boolean值。在對類型 T進(jìn)行斷言判斷時霉晕,可以使用這個接口庭再。通常稱為斷言性接口捞奕。
例如

@FunctionalInterface
public interface PredicateTest<T> {
    boolean test(T t);
}

@SpringBootTest
class LambdaTestApplicationTests {

    //借助Lambda 表達(dá)式實(shí)現(xiàn)Predicate test方法
    @Test
    public void test3(){
        PredicateTest<String> predicateTest = (str)->str.isEmpty()||str.trim().isEmpty();
        System.out.println(predicateTest.test(""));
        System.out.println(predicateTest.test(" "));
        System.out.println(predicateTest.test(" as"));
    }
}

Consumer

java.util.function.Consumer<T>接口定義了一個名叫 accept 的抽象方法,它接受泛型T拄轻,沒有返回值(void)颅围。如果需要訪問類型 T 的對象,并對其執(zhí)行某些操作恨搓,可以使用這個接口院促,通常稱為消費(fèi)性接口。

    @FunctionalInterface
    public interface ConsumerTest<T> {
        void accept(T t);
    }

  /**
     * 借助Lambda表達(dá)式實(shí)現(xiàn)Consumer accept方法
     */
    @Test
    public void test4(){
        ConsumerTest<Collection> consumerTest = collection ->{
            if (!CollectionUtils.isEmpty(collection)){
                for (Object o : collection) {
                    System.out.println(o);
                }
            }
        };

        List<String> list = new ArrayList<String>();
        list.add("1");
        list.add("2");
        list.add("3");

        consumerTest.accept(list);
    }

Function

java.util.function.Function<T, R>接口定義了一個叫作apply的方法斧抱,它接受一個泛型T的對象常拓,并返回一個泛型R的對象。如果需要定義一個Lambda辉浦,將輸入的信息映射到輸出弄抬,可以使用這個接口(比如提取蘋果的重量,或把字符串映射為它的長度),通常稱為功能性接口宪郊。

    @FunctionalInterface
    public interface FunctionTest<T,R> {
        R apply(T t);
    }

    @Test
    public void test5() {
        FunctionTest<String, String> functionTest = password -> Base64.getEncoder().encodeToString(password.getBytes());
        System.out.println(functionTest.apply("123456"));
    }

Supplier

java.util.function.Supplier<T>接口定義了一個get的抽象方法掂恕,它沒有參數(shù),返回一個泛型T的對象弛槐,這類似于一個工廠方法,通常稱為功能性接口懊亡。

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

    @Test
    public void test6(){
        int[] arr = {100,0,-50,880,99,33,-30};
        SupplierTest<Integer> s = ()->{
            int max = arr[0];
            //遍歷數(shù)組,獲取數(shù)組中的其他元素
            for (int i : arr) {
                //使用其他的元素和最大值比較
                if(i>max){
                    //如果i大于max,則替換max作為最大值
                    max = i;
                }
            }
            //返回最大值
            return max;
        };
       System.out.println(s.get().intValue());
    }

UnaryOperator

java.util.function.UnaryOperator<T> 定義了一個identity方法.
這個接口,只接收一個泛型參數(shù)T乎串,集成Function接口店枣,也就是說,傳入泛型T類型的參數(shù)灌闺,調(diào)用apply后艰争,返回也T類型的參數(shù);這個接口定義了一個靜態(tài)方法桂对,返回泛型對象的本身甩卓;

    @Test
    public void test7() {
        UnaryOperator<Integer> test = x -> x + 100;
        System.out.println(test.apply(100));
    }

BinaryOperator

java.util.function.BinaryOperator<T>接口用于執(zhí)行l(wèi)ambda表達(dá)式并返回一個T類型的返回值。

    /**
     * 接收兩個同類型的對象蕉斜,并返回一個原類型的對象
     */
    @Test
    public void test8(){
        BinaryOperator<Integer> add = (n1, n2) -> n1 + n2;
        //apply方法用于接收參數(shù)逾柿,并返回BinaryOperator中的Integer類型
        System.out.println(add.apply(3, 4));
    }

BiConsumer

這個接口接收兩個泛型參數(shù),跟Consumer一樣宅此,都有一個 accept方法机错,只不過,這里的父腕,接收兩個泛型參數(shù)弱匪,對這兩個參數(shù)做下消費(fèi)處理;使用這個函數(shù)式接口的終端操作有map的遍歷璧亮;下面看下面的例子萧诫,兩個參數(shù)消費(fèi)數(shù)據(jù)的.可以看到斥难,Map接口的終端操作,forEach的參數(shù)就是BiConsumer函數(shù)接口帘饶,對HashMap 的數(shù)據(jù)進(jìn)行消費(fèi)哑诊;BiConsumer函數(shù)接口還有一個默認(rèn)函數(shù),andThen及刻,接收一個BiConsumer接口镀裤,先執(zhí)行本接口的,再執(zhí)行傳入的參數(shù)缴饭。

Map<String, String> map = new HashMap<>();
        map.put("a", "a");
        map.put("b", "b");
        map.put("c", "c");
        map.put("d", "d");
        map.forEach((k, v) -> {
            System.out.println(k);
            System.out.println(v);
        });

方法引用

有時候我們不是必須要自己重寫某個匿名內(nèi)部類的方法暑劝,我們可以可以利用 lambda表達(dá)式的接口快速指向一個已經(jīng)被實(shí)現(xiàn)的方法。 調(diào)用特定方法的lambda表達(dá)式的一種快捷寫法茴扁,可以讓你重復(fù)使用現(xiàn)有的方法定義铃岔,并像lambda表達(dá)式一樣傳遞他們。

語法:
方法歸屬者::方法名
靜態(tài)方法的歸屬者為類名峭火,普通方法歸屬者為對象

指向靜態(tài)方法的方法引用

public void test(){
     Consumer<String> consumer = (String s) -> Integer.parseInt(s);
        Consumer<String> consumer1 = Integer::parseInt;
}

指向?qū)ο蟮膶?shí)例方法引用

StringBuilder stringBuilder = new StringBuilder();
  Consumer<String> consumer = (String s) -> stringBuilder.append(s);
  Consumer<String> consumer1 = stringBuilder::append;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末毁习,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子卖丸,更是在濱河造成了極大的恐慌纺且,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稍浆,死亡現(xiàn)場離奇詭異载碌,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)衅枫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門嫁艇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人弦撩,你說我怎么就攤上這事步咪。” “怎么了益楼?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵猾漫,是天一觀的道長。 經(jīng)常有香客問我感凤,道長悯周,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任陪竿,我火速辦了婚禮禽翼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己闰挡,他們只是感情好仇矾,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著解总,像睡著了一般。 火紅的嫁衣襯著肌膚如雪姐仅。 梳的紋絲不亂的頭發(fā)上花枫,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機(jī)與錄音掏膏,去河邊找鬼劳翰。 笑死,一個胖子當(dāng)著我的面吹牛馒疹,可吹牛的內(nèi)容都是我干的佳簸。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼颖变,長吁一口氣:“原來是場噩夢啊……” “哼生均!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起腥刹,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤马胧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后衔峰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體佩脊,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年垫卤,在試婚紗的時候發(fā)現(xiàn)自己被綠了威彰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡穴肘,死狀恐怖歇盼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情梢褐,我是刑警寧澤旺遮,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站盈咳,受9級特大地震影響耿眉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鱼响,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一鸣剪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦筐骇、人聲如沸债鸡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽厌均。三九已至,卻和暖如春告唆,著一層夾襖步出監(jiān)牢的瞬間棺弊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工擒悬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留模她,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓懂牧,卻偏偏與公主長得像侈净,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子僧凤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

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