你應該知道的Java8新特性之Lambda表達式

語言: CN / TW / HK

highlight: ocean theme: Chinese-red


“我正在參加「掘金·啟航計劃」”

Lambd表達式

Lambda表達式是一個匿名函數,我們可以把 Lambda 表達式理解為是一段可以傳遞的代碼(將代碼 像數據一樣進行傳遞)。可以寫出更簡潔、更靈活的代碼。作為一種更緊湊的代碼風格,使Java的語言表達能力得到了提升。

1. 格式

'->' :lambda操作符或箭頭操作符\ '->'的左邊: Lambda形參列表(其實就是接口中的抽象方法的形參列表)\ '->'的右邊: Lambda體 (其實就是 重寫的抽象方法的方法體)

2. Lambda表達式的使用

Lambda表達式的使用為了方便理解,分為6種情況介紹,通過內置函數式接口來舉例,也可以對比匿名函數來理解。

  1. 語法格式一:無參,無返回值,Lambda體只需一條語句

Runnable r2 = ()->{ System.out.println("Test1"); };

  1. 語法格式二:Lambda需要一個參數,並且無返回值

Consumer<String> con2 = (String s)->{ System.out.println(s); }; con2.accept("test2");

  1. 語法格式三:Lambda只需要一個參數時,參數的小括號可以省略,同時省略參數變量類型(類型推斷)

Consumer<String> con2 = s->{ System.out.println(s); }; con2.accept("test2");

  1. 語法格式四:Lambda需要兩個參數,並且有返回值

Comparator<Integer> com2 = (Integer o1,Integer o2) -> { return o1.compareTo(o2); };

  1. 語法格式五:當 Lambda 體只有一條語句時,return 與大括號可以省略

Comparator<Integer> com2 = (Integer o1,Integer o2) -> o1.compareTo(o2);

  1. 語法格式五:數據類型可以省 略,因為可由編譯器推斷得出, 稱為“類型推斷

// 數據類型可以省略,因為可由編 譯器推斷得出, 稱為“類型推斷 Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);

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

3. 函數式接口

3.1 什麼是函數式接口

只包含一個抽象方法的接口,稱為函數式接口。

你可以通過 Lambda 表達式來創建該接口的對象。(若 Lambda 表達式拋出一個受檢異常,那麼該異常需要在目標接口的抽象方法上進行聲明)。Lambda表達式也是依賴於函數式接口的。

我們可以在任意函數式接口上使用 @FunctionalInterface 註解, 這樣做可以檢查它是否是一個函數式接口,同時javadoc也會包含一條聲明,説明這個接口是一個函數式接口。

3.2 如何理解函數式接口

1.Java從誕生口起就是一直倡導“一切皆對象”,在Java裏面面向對象(OOP)編程是一切。但是隨着python、 scala等語言的興起和新技術的挑戰,Java不得不做出調整以便支持更加廣泛的技術要求,也即Java不但可以支持OOP還可以支持OOF(面向函數編程)

2.在函數式編程語言當中,函數被當做一等公民對待。在將函數作為一等公民的編程語言中,Lambda表達式的類型是函數。但是在Java8中,有所不同。在Java8中,Lambda表達式是對象,而不是函數,它們必須依附於一類特別的對象類型-函數式接口。

3.簡單的説,在Java8中,Lambda表達式就是一個函數式接口的實例。這就是Lambda表達式和函數式接口的關係。也就是説,只要一少對象是函數式接口的實例,那麼該對象就可以用Lambda表達式來表示。

所以以前用匿名實現類表示的現在都可以用Lambda表達式來寫。

3.3 自定義函數式接口

``` /* 這個接口裏只能寫一個方法,寫多個方法註解會爆紅,註解的作用是驗證該接口是否是函數接口 若果不寫該註解,並且接口只有一個方法則該接口也是函數式接口 / @FunctionalInterface
public interface MyInterface { void method(); }

//泛型寫法 @FunctionalInterface
public interface MyInterface { T method(T t); } ```

3.4 Java 內置四大核心函數式接口

| 函數式接口 | 參數類型 | 返回類型 | 用途 | | ------------------- | ---- | ------- | ------------------------------------------------------- | | Consumer 消費型接口 | T | void | 對類型為T的對象應用操 作,包含方法:void accept(T t) | | Supplier供給型接口 | 無 | T | 返回類型為T的對象,包含方法:T get(); | | Function函數型接口 | T | R | 對類型為T的對象應用操 作,並返回結果。結果 是R類型的對象。包含方 法:R apply(T t); | | Predicate斷定型接口 | T | boolean | 確定類型為T的對象是否 滿足某約束,並返回 boolean 值。包含方法 boolean test(T t); |

舉兩個簡單的例子

``` public void test4(){ Consume(500, new Consumer() { @Override public void accept(Integer integer) { System.out.println("消費了"+integer); } }); //lambda表達式寫法 Consume(400,money-> System.out.println("賺了"+money)); }

public static void Consume(int money, Consumer consumer){ consumer.accept(money); } ```

public void test5(){ List<String> list = Arrays.asList("sss","rrr","yyyyy","ssdasds","sd"); list = predit(list, new Predicate<String>() { @Override public boolean test(String s) { return s.length()>3; } }); System.out.println(list.toString()); //lambda表達式寫法 List<String> list1 = Arrays.asList("12","13","14","15","16","17"); list1 = predit(list1,i->Integer.parseInt(i)>14); System.out.println(list1.toString()); } public static List<String> predit(List<String> strings, Predicate<String> predicate){ List<String> list = new ArrayList<>(); for (String s:strings){ if (predicate.test(s)){ list.add(s); } } return list; } 下篇預告:Stream常用的API,歡迎繼續交流學習