第02篇 Lambda表達式

一、Lambda表達式引入

這裏以對人進行篩選的需求一步步變化來引入lambda

1. 數據準備

定義數據結構

package com.firewolf.java8.s001.lambda.leadin;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 作者:劉興 時間:2019-10-28
 **/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Person {

    /**
     * 顏色
     */
    private String color;
    /**
     * 年齡
     */
    private Integer age;
}

準備初始數據集合

private List<Person> personList = null;

@Before
public void init() {
    personList = Arrays.asList(
            new Person("yellow", 30),
            new Person("black", 50),
            new Person("black", 35),
            new Person("white", 45)
    );
}

2. 固定條件篩選

我們有一個需求,就是需要找出所有的黑種人,方法如下:

 public static List<Person> filterBlackPerson(List<Person> personList) {
     List<Person> result = new ArrayList<>();
     for (Person person : personList) {
         if ("black".equals(person.getColor())) {
             result.add(person);
         }
     }
     return result;
 }

通過這個方法就可以找出黑種人來

3.抽取變化屬性

在2中的代碼能夠找出黑種人,但是接下來,我們需要查找黃種人,這個時候,上面的代碼就不能滿足了,所以,我們可以如下寫出根據顏色查找的方法

 public static List<Person> filterPersonByColor(List<Person> personList, String color) {
     List<Person> result = new ArrayList<>();
     for (Person person : personList) {
         if (color.equals(person.getColor())) {
             result.add(person);
         }
     }
     return result;
 }

4. 其他屬性的處理

接下來,我們需要找出年齡大於某個年齡的人,可以提供如下方法

 public static List<Person> filterPersonsByAge(List<Person> personList, int age) {
     List<Person> result = new ArrayList<>();
     for (Person person : personList) {
         if (person.getAge() > age) {
             result.add(person);
         }
     }
     return result;
 }

5. 一個方法兼容多個屬性

我們看到,上面根據顏色和年齡分別查找的時候,需要調用不同的方法,那麼,能不能統一到一個方法呢?答案是可以的,如下:

public static List<Person> filterPersons(List<Person> personList, String color, Integer age, Integer filteType) {
    List<Person> result = new ArrayList<Person>();
    for (Person person : personList) {
        if ((filteType==1 && person.getColor().equals(color)) || (filteType==2 && person.getAge() > age)) {
            result.add(person);
        }
    }
    return result;
}

我們看到,可以通過一個filteType來區分是根據什麼來進行查找

6. 抽象過濾接口

如果我們的人員再增加一個屬性,需要怎麼處理呢?很容易想到的是,我們在上面接口的基礎上在添加分支。然而,如果判斷條件發生變化呢?比如年齡是小於,而不是大於怎麼辦?
解決辦法如下:

  1. 定義一個接口,用於判斷是否滿足條件
public interface PersonPredicate {
    boolean test(Person person);
}
  1. 接下來,我們可以根據自己的需求定義實現,如:
/**
  * 根據膚色過濾器
  */
 public static class PersonColorPredicate implements PersonPredicate {

     private String color;

     public PersonColorPredicate(String color) {
         this.color = color;
     }

     public boolean test(Person person) {
         return color.equals(person.getColor());
     }
 }

 /**
  * 根據給定的選擇器選擇人員信息
  */
 public static List<Person> filterPersons(List<Person> personList,
                                          PersonPredicate predicate) {
     List<Person> result = new ArrayList<>();
     for (Person person : personList) {
         if (predicate.test(person)) {
             result.add(person);
         }
     }
     return result;
 }

接下來,如果我們需要根據顏色來判斷,可以如下實現:

List<Person> persons1 = PersonUtil.filterPersons(personList, new PersonColorPredicate("yellow"));
System.out.println(persons1);

根據年齡,可以如下調用:

List<Person> persons2 = PersonUtil.filterPersons(personList, new PersonAgePredicate(36));
System.out.println(persons2);

7. 匿名內部類

上面的代碼結構已經比較清晰,然而,每增加一種過濾方式,就需要創建這麼一個類,如果某個過濾器只是使用一次,那麼還需要這麼定義的話,會造成很多類的複用性不高,同時增加了類的數量,此時,我們可以通過內部類來解決。

List<Person> persons = PersonUtil.filterPersons(personList, new PersonPredicate() {
    @Override
    public boolean test(Person person) {
        return person.getAge() > 40 && "black".equals(person.getColor());
    }
});
System.out.println(persons);

8. lambda表達式

上面的匿名內部類的方式,會讓我們能夠很靈活的進行處理,然而,會編寫一些無用代碼,比如方法的聲明等等,有沒有更簡單的辦法呢?答案是lambda表達式, 寫法如下:

List<Person> persons = PersonUtil.filterPersons(personList, person -> person.getAge() > 40 && "black".equals(person.getColor()));
    System.out.println(persons);

這種寫法是不是非常簡單,也許你現在不太看得懂,不過不要緊,我後面會一步一步展開講解

二、什麼是Lambda表達式

Lambda 是一個匿名函數,我們可以把 Lambda 表達式理解爲是一段可以傳遞的代碼(將代碼 像數據一樣進行傳遞)。可以寫出更簡潔、更 靈活的代碼。作爲一種更緊湊的代碼風格,使 Java的語言表達能力得到了提升。
例如:
一個Runnable類型的對象創建方式的演變:
java7中使用匿名內部類:

Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("hello,world");
    }
};

Lambda寫法如下:

Runnable r2 = ()-> System.out.println("Hello,World");

可以很明顯的看到,使用Lambda表達式的方式會讓代碼非常的簡潔。

lambda特點

  • 匿名——我們說匿名,是因爲它不像普通的方法那樣有一個明確的名稱:寫得少而想得多!
  • 函數——我們說它是函數,是因爲Lambda函數不像方法那樣屬於某個特定的類。但和方 法一樣,Lambda有參數列表、函數主體、返回類型,還可能有可以拋出的異常列表。
  • 傳遞——Lambda表達式可以作爲參數傳遞給方法或存儲在變量中。  簡潔——無需像匿名類那樣寫很多模板代碼

三、Lambda語法

Lambda表達式的格式爲: 函數式接口 = 參數列表 -> Lambda體
參數列表:就好比反覆 add(int a , int b) ,那麼int a, int b 就是參數列表;
Lambda體:可以理解爲函數體
函數式接口:這個在下一章詳細講解

(一) "->"操作符

  1. 在Java8中引入了一個新的操作符號“->”,該操作符被稱爲箭頭操作符或者是Lambda操作符
  2. 左側:Lambda表達式的參數列表
  3. 右側:表達式所需要執行的功能,稱爲Lambda體。

(二)幾種語法形式

  1. 語法格式一:無參數,無返回值
    public void test1() {
        Runnable r = () -> System.out.println("Hello,Lambda");
        r.run();
    }
    
  2. 語法格式二:有一個參數,沒有返回值
    public void test2() {
        Consumer<String> c = (x) -> System.out.println("Hello," + x);
        c.accept("Baby");
    }
    
  3. 語法格式三:當只有一個參數的時候,可以省略掉參數的小括號
    public void test3() {
        Consumer<String> c = x -> System.out.println("Hello," + x);
        c.accept("Test3");
    }
    
  4. 語法格式四:有兩個及以上個數參數,並且有返回值,需要使用{}把lambda體包起來
    public void test3() {
        Consumer<String> c = x -> System.out.println("Hello," + x);
        c.accept("Test3");
    }
    
  5. 語法格式五:有返回值,但是lambda體只有一條語句時,可以省略{}和return
    public void test5() {
        BinaryOperator<Long> op = (x, y) -> x + y;
    }
    
  6. 語法格式六:可以指明參數類型,默認情況下,可以不指定。注意的是,如果要指明,就都得指明
    public void test6() {
        BinaryOperator<Integer> op = (Integer x, Integer y) -> x + y;
    }
    

四、Lambda類型推斷

  1. 在Lambda表達式中的參數類型可以由編譯器推斷得出的。Lambda 表達式中無需指定類型,程序依然可以編譯;
  2. 這是因爲 javac 根據程序的上下文,在後臺 推斷出了參數的類型
  3. Lambda 表達式的類型依賴於上 下文環境,是由編譯器推斷出來的。這就是所謂的 “類型推斷”

五、注意事項

在Java7之前,匿名內部類如果訪問了同級的變量,那麼需要把這個變量使用final修飾,而在lambda中如果引用了同級別的變量,不需要用final修飾,但是不能改變其值,其實就是Java8內部自己加上的final修飾符,不用咱們自己顯示添加

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章