Lambda表達(dá)式

一、為什么使用Lambda表達(dá)式?

Lambda是一個(gè)匿名函數(shù)吞歼,我們可以把Lambda表達(dá)式理解為是一段可以傳遞的代碼(將代碼像數(shù)據(jù)一樣進(jìn)行傳遞)∷可以寫出更簡(jiǎn)潔篙骡、更靈活的代碼。作為一種更緊湊的代碼風(fēng)格丈甸,使Java的語言表達(dá)能力得到了提升糯俗。

二、Lambda表達(dá)式初識(shí)

下面我們來看一下幾個(gè)Lambda表達(dá)式的例子:

  • 從匿名類到Lambda的轉(zhuǎn)換

    Runnable r = new Runnable() {
              @Override
              public void run() {
                  System.out.println("Hello World!" );
              }
          };
    
    • Lambda表達(dá)式

      Runnable r1 = () -> System.out.println("Hello Lambda!");
      
      • 原來使用匿名內(nèi)部類作為參數(shù)傳遞

        
              TreeSet<String> ts2 = new TreeSet<>(new Comparator<String>(){
                  @Override
                  public int compare(String o1, String o2) {
                      return Integer.compare(o1.length(), o2.length());
                  }
                  
              });
        
      • Lambda表達(dá)式作為參數(shù)傳遞

TreeSet<String> ts2=new TreeSet<>(
(o1,o2) -> Integer.compare(o1.lenrth(),o2.length())
);

三睦擂、Lambda表達(dá)式引入點(diǎn)

要求:現(xiàn)在有一個(gè)員工集合得湘,要求按照員工的薪資、員工的年齡得到符合要求的員工集合顿仇。

首先淘正,我們定義一個(gè)員工類,Employee.java

public class Employee {

    private int id;
    private String name;
    private int age;
    private double salary;

    public Employee() {
    }

    public Employee(String name) {
        this.name = name;
    }

    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Employee(int id, String name, int age, double salary) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

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

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Employee [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]";
    }

}

第一種解決方案:也是我們比較常規(guī)思維臼闻,就是定義一個(gè)方法鸿吆,對(duì)應(yīng)要求得到指定的員工集合,這里我們就只看“獲取公司中工資大于 5000 的員工信息”定義的方法如下:

public List<Employee> filterEmployeeSalary(List<Employee> emps){
        List<Employee> list = new ArrayList<>();
        
        for (Employee emp : emps) {
            if(emp.getSalary() >= 5000){
                list.add(emp);
            }
        }
        
        return list;
    }
    

然后每一種要求對(duì)應(yīng)一種方法述呐,但是這種會(huì)很冗余惩淳,本來就一個(gè)條件不同,但是要寫這么多相同的代碼乓搬。這樣思犁,我們就要來優(yōu)化了,我們更好的思維就是通過設(shè)計(jì)模式來操作了缤谎。

第二種解決方案:利用設(shè)計(jì)模式中的策略模式來優(yōu)化抒倚。

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

public interface MyPredicate<T> {
    public boolean test(T t);
}

然后只要有一個(gè)要求坷澡,我就定義一個(gè)對(duì)應(yīng)的類實(shí)現(xiàn)這個(gè)接口托呕,這里依然是使用上述的要求:獲取公司中工資大于 5000 的員工信息。

public class FilterEmployeeForSalary implements MyPredicate<Employee> {

    @Override
    public boolean test(Employee t) {
        return t.getSalary() >= 5000;
    }

}

然后定義一個(gè)方法频敛。如下

public List<Employee> filterEmployee(List<Employee> emps, MyPredicate<Employee> mp){
        List<Employee> list = new ArrayList<>();
        
        for (Employee employee : emps) {
            if(mp.test(employee)){
                list.add(employee);
            }
        }
        
        return list;
    }

最后在測(cè)試代碼里面匿名內(nèi)部類项郊。

@Test
    public void test5(){
        List<Employee> list = filterEmployee(emps, new MyPredicate<Employee>() {
            @Override
            public boolean test(Employee t) {
                return t.getId() <= 103;
            }
        });
        
        for (Employee employee : list) {
            System.out.println(employee);
        }
    }

這樣就能得到要求的員工集合了。

但是這樣看來斟赚,還是有點(diǎn)冗余着降,因?yàn)槊恳粋€(gè)要求我都要?jiǎng)?chuàng)建一個(gè)類。所以拗军,Lambda表達(dá)式就引入了任洞。

第三種方案:引進(jìn)Lambda表達(dá)式

@Test
    public void test6(){
        List<Employee> list = filterEmployee(emps, (e) -> e.getAge() <= 35);
        list.forEach(System.out::println);
        
        System.out.println("------------------------------------------");
        
        List<Employee> list2 = filterEmployee(emps, (e) -> e.getSalary() >= 5000);
        list2.forEach(System.out::println);
    }

四蓄喇、Lambda表達(dá)式語法

Lambda表達(dá)式在Java語言中引入了一個(gè)新的語法元素和操作符。這個(gè)操作符為“->”,該操作符被稱為L(zhǎng)ambda操作符或箭頭操作符交掏。它將Lambda分為兩個(gè)部分:

左側(cè):指定了Lambda表達(dá)式需要的所有參數(shù)妆偏。

右側(cè):指定了Lambda體,即Lambda表達(dá)式要執(zhí)行的功能盅弛。

1钱骂、語法格式一:無參數(shù),無返回值
() -> System.out.println("Hello Lambda!");

示例代碼:

    @Test
    public void test1(){
        int num = 0;//jdk 1.7 前挪鹏,必須是 final
        
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello World!" + num);
            }
        };
        
        r.run();
        
        System.out.println("-------------------------------");
        //無參無返回值
        Runnable r1 = () -> System.out.println("Hello Lambda!");
        r1.run();
    }
2见秽、語法格式二:有一個(gè)參數(shù),并且無返回值
(x) -> System.out.println(x)

示例代碼:

@Test
    public void test2(){
        Consumer<String> con = (x) -> System.out.println(x);
        con.accept("你是大傻子讨盒!");
    }
3解取、語法格式三:若只有一個(gè)參數(shù),小括號(hào)可以省略不寫
x -> System.out.println(x)

示例代碼:

    @Test
    public void test2(){
        Consumer<String> con = x -> System.out.println(x);
        con.accept("你是大傻子催植!");
    }
4肮蛹、語法格式四:有兩個(gè)以上的參數(shù)勺择,有返回值创南,并且 Lambda 體中有多條語句
Comparator<Integer> com = (x, y) -> {
            System.out.println("函數(shù)式接口");
            return Integer.compare(x, y);
        };

示例代碼:

@Test
    public void test3(){
        Comparator<Integer> com = (x, y) -> {
            System.out.println("函數(shù)式接口");
            return Integer.compare(x, y);
        };
    }
5、語法格式五:若 Lambda 體中只有一條語句省核, return 和 大括號(hào)都可以省略不寫
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);

示例代碼:

@Test
    public void test4(){
        Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
    }
6稿辙、 語法格式六:Lambda 表達(dá)式的參數(shù)列表的數(shù)據(jù)類型可以省略不寫,因?yàn)镴VM編譯器通過上下文推斷出气忠,數(shù)據(jù)類型邻储,即“類型推斷”
(Integer x, Integer y) -> Integer.compare(x, y);

示例代碼:

    @Test
    public void test5(){
//      String[] strs;
//      strs = {"aaa", "bbb", "ccc"};
//字符串?dāng)?shù)組定義時(shí)不能分兩步寫,這樣寫是因?yàn)轭愋屯茢嗑稍搿K跃幾g才能通過的吨娜。
        List<String> list = new ArrayList<>();
// ArrayList<>的類型可以不用寫,這樣也是因?yàn)轭愋屯茢嗟摹?        show(new HashMap<>());
    }

    public void show(Map<String, Integer> map){
        
    }
語法小總結(jié):

上聯(lián):左右遇一括號(hào)省

下聯(lián):左側(cè)推斷類型省

橫批:能省則省

五淘钟、類型推斷(比較重要)

上述的語法六宦赠,Lambda表達(dá)式中的參數(shù)類型都是由編譯器推斷得出的。Lambda表達(dá)式中無需指定類型米母,程序依然可以編譯勾扭,這是因?yàn)閖avac根據(jù)程序的上下文,在后臺(tái)推斷出了參數(shù)的類型铁瞒。Lambda表達(dá)式的類型依賴于上下文環(huán)境妙色,是由編譯器推斷出來的。這就是所謂的“類型推斷”慧耍。

六身辨、函數(shù)式接口

這里就稍微講一下丐谋,下面的文章會(huì)詳細(xì)講解

1、什么是函數(shù)式接口煌珊?

(1)笋鄙、只包含一個(gè)抽象方法的接口,稱為函數(shù)式接口怪瓶。

(2)萧落、你可以通過Lambda表達(dá)式來創(chuàng)建該接口的對(duì)象。(若Lambda表達(dá)式拋出一個(gè)受檢異常洗贰,那么該異常需要在目標(biāo)接口的抽象方法上進(jìn)行聲明)找岖。

(3)、我們可以在任意函數(shù)式接口上使用@FunctionalInterface注解敛滋,這樣做可以檢查它是否是一個(gè)函數(shù)式接口许布,同時(shí)javadoc也會(huì)包含一條聲明,說明這個(gè)接口是一個(gè)函數(shù)式接口绎晃。

2蜜唾、自定義函數(shù)式接口

示例代碼:

@FunctionalInterface
public interface MyFun {
    public Integer getValue(Integer num);
}

七、Lambda練習(xí)

1庶艾、調(diào)用Collecions.sort()方法袁余,通過定制排序比較兩個(gè)Employee(先按年齡比,年齡相同按姓名比)咱揍,使用Lambda作為參數(shù)傳遞颖榜。

步驟一:先創(chuàng)建一個(gè)pojo

package com.nieshenkuan.pojo;

public class Employee {

    private int id;
    private String name;
    private int age;
    private double salary;

    public Employee() {
    }

    public Employee(String name) {
        this.name = name;
    }

    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Employee(int id, String name, int age, double salary) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

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

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public String show() {
        return "測(cè)試方法引用!";
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + id;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        long temp;
        temp = Double.doubleToLongBits(salary);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Employee other = (Employee) obj;
        if (age != other.age)
            return false;
        if (id != other.id)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "Employee [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]";
    }

}

步驟二:事先聲明一個(gè)emps員工集合

List<Employee> emps = Arrays.asList(
            new Employee(101, "張三", 18, 9999.99),
            new Employee(102, "李四", 59, 6666.66),
            new Employee(103, "王五", 28, 3333.33),
            new Employee(104, "趙六", 8, 7777.77),
            new Employee(105, "田七", 38, 5555.55)
    );

步驟三:測(cè)試實(shí)現(xiàn)要求

@Test
    public void test1(){
        Collections.sort(emps, (e1, e2) -> {
            if(e1.getAge() == e2.getAge()){
                    return e1.getName().compareTo(e2.getName());
            }else{
                return -Integer.compare(e1.getAge(), e2.getAge());
            }
        });
        
        for (Employee emp : emps) {
            System.out.println(emp);
        }
    }
2煤裙、(1)掩完、聲明函數(shù)式接口,接口中聲明抽象方法硼砰,public String getValue(String str);

(2)且蓬、聲明類TestLambda,類中編寫方法使用接口作為參數(shù)题翰,將一個(gè)字符串轉(zhuǎn)換成大寫恶阴,并作為方法的返回值。

(3)遍愿、再將一個(gè)字符串的第2個(gè)和第4個(gè)索引位置進(jìn)行截取子串存淫。

步驟一:先聲明一個(gè)函數(shù)式接口MyFunction,并接口中聲明抽象方法沼填,public String getValue(String str);

@FunctionalInterface
public interface MyFunction {
    
    public String getValue(String str);

}

步驟二:編寫一個(gè)方法處理字符串,這個(gè)方法在Lambda類中定義的桅咆。

//需求:用于處理字符串
    public String strHandler(String str, MyFunction mf){
        return mf.getValue(str);
    }

步驟三:測(cè)試,實(shí)現(xiàn)具體的要求

@Test
    public void test2(){
    //這個(gè)是測(cè)試去除字符串的前后空格
        String trimStr = strHandler("\t\t\t 你是大傻逼   ", (str) -> str.trim());
        System.out.println(trimStr);
        
    //這個(gè)是測(cè)試將字符串轉(zhuǎn)換成大寫的
        String upper = strHandler("abcdef", (str) -> str.toUpperCase());
        System.out.println(upper);
        
    //截取字符串的指定索引的字符串
        String newStr = strHandler("我大望江縣威武", (str) -> str.substring(2, 5));
        System.out.println(newStr);
    }
3坞笙、(1)岩饼、聲明一個(gè)帶兩個(gè)泛型的函數(shù)式接口荚虚,泛型類型為<T,R>T為參數(shù),R為返回值籍茧。

(2)版述、接口中聲明對(duì)應(yīng)抽象方法

(3)、在TestLambda類中聲明方法寞冯,使用接口作為參數(shù)渴析,計(jì)算兩個(gè)long型參數(shù)的和。

(4)吮龄、在計(jì)算兩個(gè)long型參數(shù)的乘積俭茧。

步驟一:聲明一個(gè)帶兩個(gè)泛型的函數(shù)式接口,泛型類型為<T,R>T為參數(shù)漓帚,R為返回值

public interface MyFunction2<T, R> {

    public R getValue(T t1, T t2);
    
}

步驟二:編寫一個(gè)方法母债,計(jì)算兩個(gè)long型的數(shù)據(jù)

//需求:對(duì)于兩個(gè) Long 型數(shù)據(jù)進(jìn)行處理
    public void op(Long l1, Long l2, MyFunction2<Long, Long> mf){
        System.out.println(mf.getValue(l1, l2));
    }

步驟三:測(cè)試,并實(shí)現(xiàn)要求尝抖。

@Test
    public void test3(){
    //實(shí)現(xiàn)兩個(gè)long型的數(shù)值的和
        op(100L, 200L, (x, y) -> x + y);
    //實(shí)現(xiàn)兩個(gè)long型的數(shù)值的積    
        op(100L, 200L, (x, y) -> x * y);
    }
    
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末毡们,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子昧辽,更是在濱河造成了極大的恐慌衙熔,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奴迅,死亡現(xiàn)場(chǎng)離奇詭異青责,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)取具,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扁耐,“玉大人暇检,你說我怎么就攤上這事⊥癯疲” “怎么了块仆?”我有些...
    開封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)王暗。 經(jīng)常有香客問我悔据,道長(zhǎng),這世上最難降的妖魔是什么俗壹? 我笑而不...
    開封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任科汗,我火速辦了婚禮,結(jié)果婚禮上绷雏,老公的妹妹穿的比我還像新娘头滔。我一直安慰自己怖亭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開白布坤检。 她就那樣靜靜地躺著兴猩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪早歇。 梳的紋絲不亂的頭發(fā)上倾芝,一...
    開封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天,我揣著相機(jī)與錄音箭跳,去河邊找鬼蛀醉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛衅码,可吹牛的內(nèi)容都是我干的拯刁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼逝段,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼垛玻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起奶躯,我...
    開封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤帚桩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后嘹黔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體账嚎,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年儡蔓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了郭蕉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡喂江,死狀恐怖召锈,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情获询,我是刑警寧澤涨岁,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站吉嚣,受9級(jí)特大地震影響梢薪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜尝哆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一秉撇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦畜疾、人聲如沸赴邻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姥敛。三九已至,卻和暖如春瞎暑,著一層夾襖步出監(jiān)牢的瞬間彤敛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來泰國打工了赌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留墨榄,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓勿她,卻偏偏與公主長(zhǎng)得像袄秩,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子逢并,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

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