JDK8新特性之Lambda表達(dá)式

前言

項(xiàng)目代碼在本級(jí)目錄下的lambda_demo

說(shuō)起 JDK8 的新特性帆疟,總繞不過(guò)兩個(gè),一個(gè)是流(stream)自赔,一個(gè)就是 lambda表達(dá)式绍妨。

因?yàn)?引入 lambda 表達(dá)式 可以說(shuō)是 Java 的一次革命性的嘗試他去,因?yàn)閘ambda表達(dá)式是函數(shù)式編程灾测,把函數(shù)方法參數(shù)化。而之前的 Java 是面向?qū)ο蟮脑始幔浅7浅?yán)格的。甚至可以說(shuō)是呆板鲜结。

但是 Java的之前的那種風(fēng)格 或者說(shuō)設(shè)定并不能說(shuō)是他的弊端精刷,對(duì)于剛?cè)腴T(mén)編程的人或者初中級(jí)的Java編程者來(lái)說(shuō)蔗候,Java的這種語(yǔ)法更是他的優(yōu)勢(shì)锈遥,尤其是企業(yè)級(jí)的應(yīng)用來(lái)說(shuō)所灸,易于維護(hù)和易于理解比運(yùn)行效率更重要。這也是Java最近幾年被作為企業(yè)級(jí)應(yīng)用程序的首選語(yǔ)言的原因钾唬,因?yàn)檎l(shuí)都不能保證和你共事的人都是頂尖的人才抡秆,你寫(xiě)的他都懂儒士,他寫(xiě)的你都懂

但是乍桂,隨著軟件的發(fā)展,會(huì)編程的人越來(lái)越多睹酌,門(mén)檻不斷升高权谁,你不學(xué)習(xí),不進(jìn)步憋沿,那你就只能被退休旺芽。連Java都開(kāi)始改變他的語(yǔ)法了,你都不準(zhǔn)備改變嗎

這篇文章辐啄,我不準(zhǔn)備直接講語(yǔ)法采章,因?yàn)榛久總€(gè)人第一次看到 lambda語(yǔ)法直接就懵的。講了也記不住悯舟,不理解,不會(huì)用砸民,到時(shí)候講了也是白講

我用個(gè)小例子講述下抵怎,我們?cè)谧鯿rud的時(shí)候,都是怎么完成功能的岭参。然后怎么根據(jù)需求變更版本反惕,最后再講lambda表達(dá)式

如果想運(yùn)用lambda表達(dá)式,使用JDK8的另一個(gè)新特性 Stream 演侯,效果會(huì)更好姿染,可以參考我的這篇文章 JDK8 新特性stream

如果圖片加載不出來(lái),可以在github上看秒际,這里的文章更全更新更及時(shí)悬赏。lambda表達(dá)式

背景

假設(shè)我們現(xiàn)在在做一個(gè) 學(xué)生管理系統(tǒng)(類(lèi)似于這種的管理系統(tǒng),我想基本應(yīng)該都做過(guò))程癌。

我們?cè)趯?xiě)service部分舷嗡,根據(jù)需求來(lái)編寫(xiě)相應(yīng)的方法

項(xiàng)目結(jié)構(gòu)

Ps: 沒(méi)有用嚴(yán)格的項(xiàng)目結(jié)構(gòu),但是大致懂就行啦

image

基本類(lèi)

package com.leosanqing;

import lombok.AllArgsConstructor;
import lombok.Data;

/**
 * @Author: leosanqing
 * @Date: 2020/1/17 上午7:54
 * @Package: com.leosanqing
 * @Description: 學(xué)生類(lèi)
 */
@Data
@AllArgsConstructor
public class Student {
    private String name;
    private int age;
    private String sex;
    /**
     * 課程
     */
    private String subject;
    private int height;
    private int weight;
    private String teacher;

}

list列表

學(xué)生信息分別都是這些

private static List<Student> studentList = new ArrayList<Student>() {
        {
            add(new Student("張三豐", 20, "男", "體育",
                    180, 75, "太上老君"));
            add(new Student("張無(wú)忌", 18, "男", "語(yǔ)文",
                    178, 73, "文曲星"));
            add(new Student("趙敏", 17, "女", "數(shù)學(xué)",
                    170, 50, "太白金星"));
            add(new Student("金毛獅王", 25, "男", "體育",
                    176, 80, "太白金星"));
            add(new Student("周芷若", 16, "女", "語(yǔ)文",
                    168, 48, "太上老君"));
            add(new Student("張三", 21, "男", "英語(yǔ)",
                    172, 65, "如來(lái)"));
            add(new Student("趙勇", 26, "男", "體育",
                    188, 80, "太上老君"));


        }
    };

版本一

我找出所有學(xué)生中的女學(xué)生

這個(gè)應(yīng)該很簡(jiǎn)單,只需要將寫(xiě)個(gè)過(guò)濾條件就行

    /**
     * 版本一
     * 找出女學(xué)生
     *
     * @return
     */
    public static List<Student> getFemaleStudent(List<Student> studentList) {
        List<Student> students = new ArrayList<>();
        for (Student student : studentList) {
            if ("女".equals(student.getSex())) {
                students.add(student);
            }
        }
        return students;
    }

版本二

上面的太死了嵌莉,只能找出女學(xué)生进萄。我要能根據(jù)輸入的性別來(lái)找。將性別作為參數(shù)傳進(jìn)入

    /**
     * 版本二
     * <p>
     * 根據(jù)輸入性別查找學(xué)生
     *
     * @param studentList
     * @param sex
     * @return
     */
    public static List<Student> getStudentBySex(List<Student> studentList, String sex) {
        List<Student> students = new ArrayList<>();
        for (Student student : studentList) {
            if (sex.equals(student.getSex())) {
                students.add(student);
            }
        }
        return students;
    }

版本三

你這個(gè)功能太少了锐峭,我要不僅能 根據(jù)輸入的年齡或者老師查找中鼠,也能根據(jù)年齡查找的功能的方法。那我們就多設(shè)置幾個(gè)參數(shù)沿癞,把那些要求都作為參數(shù)傳進(jìn)入


    /**
     * 版本三
     * <p>
     * 根據(jù)輸入的年齡或者老師查找援雇。true表示使用年齡,false表示使用老師這個(gè)參數(shù)
     *
     * @param studentList
     * @param age
     * @return
     */
    public static List<Student> getStudentByAgeOrTeacher(List<Student> studentList, int age,
                                                         String teacher, boolean ageOrTeacher) {
        List<Student> students = new ArrayList<>();
        for (Student student : studentList) {
            if ((ageOrTeacher && student.getAge() > age)
                    || !ageOrTeacher && teacher.equals(student.getTeacher())
                ) {

                students.add(student);
            }
        }
        return students;
    }

版本四

版本三解決了一個(gè)需求椎扬,但是如果我的需求復(fù)雜惫搏,要求多呢具温?我就要輸入n多個(gè)參數(shù)

這個(gè)時(shí)候,我們就要將其抽象一下筐赔,使用面向?qū)ο蟮乃枷胂承伞S靡幌?策略者模式。(嚴(yán)格意義上說(shuō)茴丰,雖然用Java編程达皿,但是很多人根本連Java都沒(méi)有入門(mén),因?yàn)闆](méi)有用過(guò) 面向?qū)ο蟮乃枷牖呒纾救敲嫦蜻^(guò)程 峦椰。你自己定義過(guò) 接口,使用過(guò)多態(tài)嗎汰规?使用過(guò)抽象類(lèi)嗎汤功?使用過(guò)繼承嗎?Spring框架很強(qiáng)大控轿,強(qiáng)大到很多人都變"笨"了,我只需要crud冤竹,其他的大部分框架已經(jīng)幫忙做了,我也不需要怎么設(shè)計(jì)茬射,不用考慮怎么抽出通用的類(lèi)和方法)

我們定義一個(gè)接口

package com.leosanqing.predicate;

import com.leosanqing.bean.Student;

/**
 * @Author: leosanqing
 * @Date: 2020/1/19 下午11:41
 * @Package: com.leosanqing.service
 * @Description: 學(xué)生條件接口
 */
public interface StudentPredicate {
    boolean filter(Student student);
}

來(lái)兩個(gè)實(shí)現(xiàn)類(lèi)

package com.leosanqing.predicate;

import com.leosanqing.bean.Student;

/**
 * @Author: leosanqing
 * @Date: 2020/1/19 下午11:49
 * @Package: com.leosanqing.predicate
 * @Description: 根據(jù)年齡過(guò)濾
 */
public class AgePredicate implements StudentPredicate {
    @Override
    public boolean filter(Student student) {
        return student.getAge() > 20;
    }
}

package com.leosanqing.predicate;

import com.leosanqing.bean.Student;

/**
 * @Author: leosanqing
 * @Date: 2020/1/19 下午11:51
 * @Package: com.leosanqing.predicate
 * @Description: 根據(jù)老師過(guò)濾
 */
public class TeacherPredicate implements StudentPredicate{
    @Override
    public boolean filter(Student student) {
        return "如來(lái)".equals(student.getTeacher());
    }
}

    /**
     * 版本四
     *
     * 使用多態(tài)完成,使用策略者模式
     * @param studentList
     * @param predicate
     * @return
     */
    public static List<Student> filterStudent(List<Student> studentList, StudentPredicate predicate) {
        List<Student> students = new ArrayList<>();
        for (Student student : studentList) {
            if(predicate.filter(student)){
                students.add(student);
            }
        }
        return students;
    }

版本五

我們覺(jué)得這個(gè)這個(gè)太麻煩了冒签,我們只想用的時(shí)候再對(duì)其進(jìn)行實(shí)現(xiàn)在抛。因此便有了匿名內(nèi)部類(lèi)

    /**
     * 版本五,使用匿名內(nèi)部類(lèi)萧恕。 使用Test測(cè)試的時(shí)候就是在使用這個(gè)方法刚梭,我們?cè)谑褂玫臅r(shí)候才去實(shí)現(xiàn)具體的方法
     */
    @Test
    public void anonymousInnerClass(){
        final List<Student> studentList = StudentService.getStudentList();
        final List<Student> students = StudentService.filterStudent(studentList, new StudentPredicate() {
            @Override
            public boolean filter(Student student) {
                return "如來(lái)".equals(student.getTeacher());
            }
        });
        System.out.println(JSON.toJSONString(students,true));

    }

版本六

匿名內(nèi)部類(lèi)已經(jīng)夠簡(jiǎn)單的了吧?但是我們還想再簡(jiǎn)單點(diǎn)票唆,這個(gè)時(shí)候 lambda就出來(lái)了

    /**
     * 版本六朴读,使用lambda
     */
    @Test
    public void lambda(){
        final List<Student> studentList = StudentService.getStudentList();
        final List<Student> students = StudentService.filterStudent(studentList, student -> "如來(lái)".equals(student.getTeacher()));
        System.out.println(JSON.toJSONString(students,true));

    }

我們看,lambda表達(dá)式其實(shí)和匿名內(nèi)部類(lèi)結(jié)構(gòu)有點(diǎn)類(lèi)似走趋。

通過(guò)這樣的介紹衅金,現(xiàn)在你應(yīng)該知道 lambda 表達(dá)式 其實(shí)也沒(méi)有那么難理解了吧,你就把他當(dāng)成匿名函數(shù)來(lái)理解會(huì)容易的多

在我上大學(xué)的時(shí)候,那個(gè)時(shí)候還在講Java的GUI簿煌,當(dāng)我們定義按鈕和何種監(jiān)聽(tīng)器的時(shí)候氮唯,為了方便,幾乎都用匿名內(nèi)部類(lèi)來(lái)完成姨伟。

再比如我們使用 多線程的時(shí)候

new Thread(new Runnable() {
            @Override
            public void run() {
                
            }
        });

那我們就可以用lambda寫(xiě)成這樣子的

new Thread(()->{
  //這個(gè)里面寫(xiě)的就是run方法里面的內(nèi)容
});

接口

函數(shù)式接口 參數(shù)類(lèi)型 返回類(lèi)型 用途
Consumer(消費(fèi)型接口) T void 對(duì)類(lèi)型為T(mén)的對(duì)象應(yīng)用操作惩琉。void accept(T t)
Supplier(供給型接口) 無(wú) T 返回類(lèi)型為T(mén)的對(duì)象。 T get();
Function(函數(shù)型接口) T R 對(duì)類(lèi)型為T(mén)的對(duì)象應(yīng)用操作并返回R類(lèi)型的對(duì)象夺荒。R apply(T t);
Predicate(斷言型接口) T boolean 確定類(lèi)型為T(mén)的對(duì)象是否滿(mǎn)足約束瞒渠。boolean test(T t);

上面是我們常用到的四種函數(shù)接口良蒸,我們之前代碼展示的就是 Predicate的接口,我們使用的 filter方法伍玖,返回的是一個(gè) bool值

如果想了解或者實(shí)戰(zhàn)诚啃,使用stream編程的時(shí)候會(huì)更深刻,可以看看我的這篇文章私沮。 Stream流編程

那么如果我們自己定義一個(gè)你能夠使用 lambda表達(dá)式的接口始赎,應(yīng)該注意什么呢

條件

接口中有且只有一個(gè)抽象方法

我們看到我們的代碼中,我們的接口中只有 filter 這一個(gè)抽象方法仔燕。

我們自己定義的時(shí)候造垛,可以使用jdk8 新提供的一個(gè)注解 @FunctionalInterface ,這個(gè)沒(méi)有其他作用晰搀,就是標(biāo)示他是一個(gè)函數(shù)式接口五辽,也就是能夠直接用來(lái)寫(xiě)lambda表達(dá)式的接口。不滿(mǎn)足條件的就會(huì)報(bào)錯(cuò)

比如我們接口中沒(méi)有抽象方法外恕,或者抽象方法有兩個(gè)及以上就會(huì)報(bào)以下的錯(cuò)誤

image

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-CamnJZPL-1581398448695)(img/Xnip2020-02-11_12-42-34.jpg)]

方法引用

我們有時(shí)候會(huì)看到這樣的代碼

    public void peekTest() {
        studentList.stream()
                .peek(System.out::println)
                .sorted(Comparator.comparingInt(Student::getAge))
                .forEach(stu -> System.out.println(JSON.toJSONString(stu, true)));
    }

第一次看這個(gè)的時(shí)候肯定會(huì)懵杆逗,這個(gè)雙冒號(hào)是什么鬼?

其實(shí)這個(gè)是 方法引用

定義

方法引用 是 lambda表達(dá)式的一種快捷寫(xiě)法鳞疲,記住他是lambda的一種快捷寫(xiě)法罪郊,要先能寫(xiě)成lambda才能寫(xiě)成 方法引用

Stu              ::             getAge
目標(biāo)引用            雙冒號(hào)         方法名

類(lèi)型

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

        public void test1(){
            Consumer<String> consumer1 = number -> Integer.parseInt(number);
    
            Consumer<String> consumer2 = Integer::parseInt;
            
        }
    
  1. 指向現(xiàn)有對(duì)象的實(shí)例方法的方法引用

        public void test2(){
            Consumer<String> consumer1 = number -> number.length();
    
            Consumer<String> consumer2 = String::length;
    
        }
    
  1. 指向任意類(lèi)型實(shí)例方法的方法引用

        public void test3(){
    
            StringBuilder stringBuilder = new StringBuilder();
            Consumer<String> consumer1 = number -> stringBuilder.append(number);
    
            Consumer<String> consumer2 = stringBuilder::append;
    
        }
    
    
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市尚洽,隨后出現(xiàn)的幾起案子悔橄,更是在濱河造成了極大的恐慌,老刑警劉巖腺毫,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件癣疟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡潮酒,警方通過(guò)查閱死者的電腦和手機(jī)睛挚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)急黎,“玉大人扎狱,你說(shuō)我怎么就攤上這事∪郏” “怎么了委乌?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)荣回。 經(jīng)常有香客問(wèn)我遭贸,道長(zhǎng),這世上最難降的妖魔是什么心软? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任壕吹,我火速辦了婚禮著蛙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘耳贬。我一直安慰自己踏堡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布咒劲。 她就那樣靜靜地躺著顷蟆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪腐魂。 梳的紋絲不亂的頭發(fā)上帐偎,一...
    開(kāi)封第一講書(shū)人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音蛔屹,去河邊找鬼削樊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛兔毒,可吹牛的內(nèi)容都是我干的漫贞。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼育叁,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼迅脐!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起擂红,我...
    開(kāi)封第一講書(shū)人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤仪际,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后昵骤,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肯适,尸身上長(zhǎng)有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
  • 文/蒙蒙 一完丽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拇舀,春花似錦逻族、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至要拂,卻和暖如春抠璃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宇弛。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工鸡典, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人枪芒。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓彻况,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親舅踪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子纽甘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

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

  • 描述 Lambda 表達(dá)式,也可稱(chēng)為閉包抽碌,它是推動(dòng) Java 8 發(fā)布的最重要新特性悍赢。Lambda 允許把函數(shù)作為...
    初心myp閱讀 322評(píng)論 0 0
  • Java Lambda概要 Java Lambda表達(dá)式是一種匿名函數(shù);它沒(méi)有聲明的方法货徙,即沒(méi)有訪問(wèn)修飾符左权、返回值...
    小波同學(xué)閱讀 1,946評(píng)論 0 47
  • 簡(jiǎn)介 概念 Lambda 表達(dá)式可以理解為簡(jiǎn)潔地表示可傳遞的匿名函數(shù)的一種方式:它沒(méi)有名稱(chēng),但它有參數(shù)列表痴颊、函數(shù)主...
    劉滌生閱讀 3,204評(píng)論 5 18
  • 我們常說(shuō)心靈和身體總有一個(gè)在路上蠢棱,八月底與朋友相約九月初來(lái)一場(chǎng)自駕游锌杀,我滿(mǎn)懷興奮與期待,這么多年一直都說(shuō)要自駕泻仙,不...
    君子之交3036閱讀 292評(píng)論 0 0
  • 只有你明白了生命的意義糕再,生命的畫(huà)卷才向你鋪展開(kāi)來(lái)。 生老病死,是生命之河在流淌。 無(wú)論命運(yùn)待我怎樣室梅,我都對(duì)它充滿(mǎn)敬...
    下河上海閱讀 389評(píng)論 0 14