Java8之Lambda表達式使用解讀

一、初識Lambda

Lambda 表達式(lambda expression)是一個匿名函數,Lambda表達式基於數學中的λ演算得名,直接對應於其中的lambda抽象(lambda abstraction),是一個匿名函數,即沒有函數名的函數。

Lambda表達式可以表示閉包(註意和數學傳統意義上的不同)。

這是來自萬能的百度百科對於lambda表達式的解釋,懂的都懂,不懂的真是聽君一席話,如聽一席話。

Lambda表達式在Java8中及後續版本占據瞭舉足輕重的地位,對於函數式編程來說是必不可少的一環,所以學會lambda表達式,對於使用Java8的同學是必經之路

舉個栗子,Java8中的stream你們不會沒有用過吧?什麼?你還在用Java7?阿巴阿巴阿巴…好吧,那我直接用代碼舉個栗子吧。

1.Lambda栗子

需求是這樣的,二哥第一次來到瞭大城市,到瞭大城市就去瞭 大 保 健,這個時候肯定就要選女朋友瞭,根據姓名、大小、價位等,咳咳咳…說錯瞭,是根據姓名、鞋碼、身價來選擇,二哥要求經理讓這些女朋友按照身價來正序排隊站好。

@Data
@AllArgsConstructor
public class Girl {

    private String name;

    private Double size;

    private Double price;
}
    public static void main(String[] args) {
         ArrayList<Girl> list = Lists.newArrayList();
        list.add(new Girl("露西",33d,2000d));
        list.add(new Girl("格蕾絲",36d,3000d));
        list.add(new Girl("安娜",28d,1500d));
        list.add(new Girl("克瑞斯",31d,1800d));
        //匿名函數類實現
        list.sort(new Comparator<Girl>() {
            @Override
            public int compare(Girl o1, Girl o2) {
                return o1.getPrice().compareTo(o2.getPrice());
            }
        });
        list.stream().forEach(System.out::println);

        Collections.shuffle(list);
        System.out.println("-------------我是分隔符-----------------");

        //lambda實現
        list.sort((Girl g1, Girl g2)-> g1.getPrice().compareTo(g2.getPrice()));
        list.stream().forEach(System.out::println);
    }

不難看出,兩種方式都實現瞭讓女朋友們按照身價排隊。那麼我們著重來看看Lambda表達式。

2.Lambda表達式的組成

(Girl g1, Girl g2)-> g1.getPrice().compareTo(g2.getPrice());
  • 參數列表:本例中是兩個Mask對象的參數,采用的是Comparator接口中compare方法的參數。
  • 箭頭:->把參數列表和主體分隔為兩個部分。
  • 主體:本例中是把比較價格的表達式作為Lambda表達式的返回。主體可以修改成另外一種寫法,含義是一樣的:
list.sort((Girl g1, Girl g2)-> {
	return g1.getPrice().compareTo(g2.getPrice());
});

由此可以看出,lambda表達式語法格式可以分為兩種:

  • 1.(parameters) -> expression //參數列表加單條表達式
  • 2.(parameters) ->{ statements; } //參數列表加花括號和多條語句

以下是lambda表達式的重要特征:

  • 可選類型聲明:不需要聲明參數類型,編譯器可以統一識別參數值。
  • 可選的參數圓括號:一個參數無需定義圓括號,但多個參數需要定義圓括號。
  • 可選的大括號:如果主體包含瞭一個語句,就不需要使用大括號。
  • 可選的返回關鍵字:如果主體隻有一個表達式返回值則編譯器會自動返回值,大括號需要指定表達式返回瞭一個數值。

3.舉個栗子

// 1. 不需要參數,返回值為 5  
() -> 5  
  
// 2. 接收一個參數(數字類型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2個參數(數字),並返回他們的差值  
(x, y) -> x – y  
  
// 4. 接收2個int型整數,返回他們的和  
(int x, int y) -> x + y  
  
// 5. 接受一個 string 對象,並在控制臺打印,不返回任何值(看起來像是返回void)  
(String s) -> System.out.print(s)

// 6.接受兩個int參數,多條語句進行比較返回1或者0
(int s1, int s2)->{
	if(s1>s2){
		return 0;
	}
	return 1;
}

二、Lambda更進一步

1.類型推斷

 	  //lambda實現
      list.sort((Girl g1, Girl g2)-> g1.getPrice().compareTo(g2.getPrice()));

      //lambda簡化寫法
      list.sort((g1, g2)-> g1.getPrice().compareTo(g2.getPrice()));

2.方法引用

方法引用通過方法的名字來指向一個方法。方法引用可以使語言的構造更緊湊簡潔,減少冗餘代碼。方法引用使用一對冒號 :: 。

Comparator<Girl> comparator = (g1, g2)-> g1.getPrice().compareTo(g2.getPrice());
//方法引用
Comparator.comparing(Girl::getPrice);

當你需要方法引用時,目標引用放在分隔符::前,方法的名稱放在分隔符::後。

比如,上面的Girl::getPrice,就是引用瞭Mask中定義的getPrice方法。

方法名稱後不需要加括號,因為我們並沒有實際調用它。

方法引用提高瞭代碼的可讀性,也使邏輯更加清晰,在一些stream流中,相信大傢也經常會用到。

例如:

 List<String> collect = list.stream().map(Girl::getName).collect(Collectors.toList());
 list.stream().forEach(System.out::println);

我們看到第一行通過Girl::getName方法引用,我們收集到瞭所有女孩的名字,那麼第二行我們通過System.out::println打印出瞭list中的所有對象。

舉個栗子:

我們先改造一下實體類,增加瞭空參構造函數,增加瞭幾個方法,有靜態方法和非靜態方法。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Girl {

    private String name;

    private Double size;

    private Double price;


    //Supplier是jdk1.8的接口,這裡和lamda一起使用瞭
    public static Girl create(final Supplier<Girl> supplier) {
        return supplier.get();
    }

    public static void makeUp(final Girl girl) {
        System.out.println("化妝  " + girl.toString());
    }

    public void follow(final Girl another) {
        System.out.println("Following the " + another.toString());
    }

    public void dressUp() {
        System.out.println("更衣 " + this.toString());
    }
}

4種不同的方法引用

	  //構造器引用:它的語法是Class::new,或者更一般的Class< T >::new實例如下:
       Girl girl = Girl.create(Girl::new);
       List<Girl> girls = Arrays.asList(girl);

       //靜態方法引用:它的語法是Class::static_method,實例如下:
       girls.forEach(Girl::makeUp);

       //特定類的任意對象的方法引用:它的語法是Class::method實例如下:
       girls.forEach(Girl::dressUp);

       //特定對象的方法引用:它的語法是instance::method實例如下:
       girls.forEach(girl::follow);

到這裡,大傢對lambda表達式應該有瞭初步的認識,可以在平時編碼中多使用,畢竟實踐出真知!!

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: