Java 8 - 02 Lambda Expression
文章目錄
- Pre
- Lambda 初探
- lambda語法
- 函數式接口
- 函數描述符
Pre
上一節 Java 8 - 01 優雅編程 lambda 以及 @FunctionalInterface注解一點通
中有的時候使用了匿名類來表示不同的行為 ,代碼比較啰嗦
Java 8中如何解決這個問題呢?
Lambda表達式 。 它可以讓你很簡潔地表示一個行為或傳遞代碼。現在你可以把Lambda表達式看作匿名功能,它基本上就是沒有聲明名稱的方法,但和匿名類一樣,它也可以作為參數傳遞給一個方法。
Lambda 初探
可以把Lambda表達式理解為簡潔地表示可傳遞的匿名函數的一種方式:它沒有名稱,但它有參數列表、函數主體、返回類型,可能還有一個可以拋出的異常列表。
- 匿名——我們說匿名,是因為它不像普通的方法那樣有一個明確的名稱
- 函數—— Lambda函數不像方法那樣屬于某個特定的類。但和方法一樣,Lambda有參數列表、函數主體、返回類型,還可能有可以拋出的異常列表。
- 傳遞——Lambda表達式可以作為參數傳遞給方法或存儲在變量中。
- 簡潔——無需像匿名類那樣寫很多模板代碼。
我們來看下lambda的語法
( o1, o2) -> o1.getJob().compareTo(o2.getJob());-
( o1, o2) : 參數 ,1個參數的話,可以省略括號
-
-> : 箭頭,固定寫法
-
o1.getJob().compareTo(o2.getJob()) : lambda主題
我們看看使用lambda之前的語法
Comparator<Enginner> enginnerComparator = new Comparator<Enginner>() {@Overridepublic int compare(Enginner o1, Enginner o2) {return o1.getJob().compareTo(o2.getJob());}};用了Lambda表達式:
Comparator<Enginner> enginnerComparator = ( o1, o2) -> o1.getJob().compareTo(o2.getJob());所以說:
- 參數列表——這里它采用了 Comparator 中 compare 方法的參數,兩個 Enginner
- 箭頭——箭頭 -> 把參數列表與Lambda主體分隔開
- Lambda主體——比較兩個 Enginner 的職位。表達式就是Lambda的返回值
為啥這里連return也沒有了呢? 如果 你加了 {} , 就必須要帶上return了
( o1, o2) -> { return o1.getJob().compareTo(o2.getJob()) ;}如果沒有 { } 就一行代碼,其實是可以省略的,lambda中是可以省略的
為了更進一步的理解lambda,我們接著看幾個常見的lambda表達式
Function<String,Integer> flambda = (String s) -> s.length();第一個Lambda表達式具有一個 String 類型的參數并返回一個 int 。Lambda沒有 return 語句,因為已經隱含了 return 。
這個語句的功能,輸入一個字符串,返回字符串的長度 。 如果你需要定義一個Lambda,將輸入對象的信息映射 到輸出 , java.util.function.Function<T, R> 接口 是你的不二選擇
Predicate<Enginner> predicate= (Enginner e) -> e.getAge() > 30 ;
第二個Lambda 表達式有一個 Enginner類 型的參數并返回一 個 boolean (Enginner 的年齡是否大于30)
在你需要表示一個涉及類型 T 的布爾表達式時,就可以使用java.util.function.Predicate<T>這個接口
(int x,int y ) -> {System.out.println(x);System.out.println(y);};
第三個Lambda表達式具有兩個 int 類型的參數而沒有返回值( void 返回)。注意Lambda表達式可以包含多行語句,這里是兩行.
() -> 18
第四個Lambda 表達式沒有參數,返回一個 int
Comparator<Enginner> comparator2 = ( o1, o2) -> o1.getJob().compareTo(o2.getJob());
第五個Lambda表達式具有兩個 Enginner類型的參數,返回一個 int :比較兩個 Enginner的職業。
lambda語法
Lambda 的基本語法是
(parameters) -> expression或(請注意語句的花括號)
(parameters) -> { statements; }小測驗一把
根據上述語法規則,以下哪個不是有效的Lambda表達式?
(1) () -> {} (2) () -> "Raoul" (3) () -> {return "Mario";} (4) (Integer i) -> return "Alan" + i; (5) (String s) -> {"IronMan";}答案:只有4和5是無效的Lambda。
(1) 這個Lambda沒有參數,并返回 void 。它類似于主體為空的方法: public void run() {} 。
(2) 這個Lambda沒有參數,并返回 String 作為表達式。
(3) 這個Lambda沒有參數,并返回 String (利用顯式返回語句)。
(4) return 是一個控制流語句。要使此Lambda有效,需要使花括號,如下所示: (Integer i) -> {return "Alan" + i;} 。 還得有 分號
(5)“Iron Man”是一個表達式,不是一個語句。要使此Lambda有效,你可以去除花括和分號,如下所示: (String s) -> "Iron Man" 。或者如果你喜歡,可以使用顯式返回語句,如下所示: (String s)->{return "IronMan";} 。
再舉幾個例子 ,加深下印象:
布爾表達式
(List<String> list) -> list.isEmpty()創建對象
() -> new Apple(10)消費一個對象
(Enginner a) -> {System.out.println(a.getAge()); }從一個對象中選擇/抽取
(String s) -> s.length()組合兩個值
(int a, int b) -> a * b比較兩個對象
Comparator<Enginner> comparator2 = ( o1, o2) -> o1.getJob().compareTo(o2.getJob());函數式接口
enginnerTest.findEnginner(enginnerList,engineer -> {return "Java".equals(engineer.getJob());});就可以優化為
enginnerTest.findEnginner(enginnerList,engineer -> "Java".equals(engineer.getJob()) );那到底在哪里可以使用Lambda呢?你可以在函數式接口上使用Lambda表達式。在上面的代
碼中,我們把 Lambda表達式作為第二個參數傳給 filter 方法,因為它這里需要EnginnerFilter ,而這是一個函數式接口.
EnginnerFilter就是一個標準的函數式接口。 因為 EnginnerFilter 僅僅定義了一個抽象方法getMatchedEnginner.
一言以蔽之,函數式接口就是只定義一個抽象方法的接口
實際上 接口現在還可以擁有 默認方法(即在類沒有對方法進行實現時,其主體為方法提供默認實現的方法)。哪怕有很多默認方法,只要接口只定義了一個抽象方法抽象方法,它就仍然是一個函數式接口
測試下
下面哪些接口是函數式接口?
public interface Adder{int add(int a, int b); } public interface SmartAdder extends Adder{int add(double a, double b); } public interface Nothing{ }- 只有 Adder 是函數式接口。
- SmartAdder 不是函數式接口,因為它定義了兩個叫作 add 的抽象方法(其中一個是從
Adder 那里繼承來的)。 - Nothing 也不是函數式接口,因為它沒有聲明抽象方法
用函數式接口可以干什么呢?Lambda表達式允許你直接以內聯的形式為函數式接口的抽象方法提供實現,并把整個表達式作為函數式接口的實例(具體說來,是函數式接口一個具體實現的實例)。
當然了,你用匿名內部類也可以完成同樣的事情,只不過比較笨拙:需要提供一個實現,然后再直接內聯將它實例化。
舉個例子
// 使用lambdaRunnable r1 = () -> System.out.println("Hello artisan Lambda");// 使用匿名內部類Runnable r2 = new Runnable() {@Overridepublic void run() {System.out.println("Hello artisan Inner");}};r1.run();r2.run();函數描述符
函數式接口的抽象方法的簽名基本上就是Lambda表達式的簽名。我們將這種抽象方法叫作函數描述符。
舉個例子
Runnable 接口可以看作一個什么也不接受什么也不返回( void )的函數的簽名,因為它只有一個叫作 run 的抽象方法,這個方法什么也不接受,什么也不返回( void )。
(Enginner o1, Enginner o2) -> o1.getJob().compareTo(o2.getJob());代表接受兩個 Enginner 作為參數且返回 int 的函數。
Lambda表達式可以被賦給一個變量,或傳遞給一個接受函數式接口作為參數的方法 ,當然這個Lambda表達式的簽名要和函數式接口的抽象方法一樣
比如 你可以像下面這樣直接把一個Lambda傳給 process 方法:
public void process(Runnable r){r.run(); }process(() -> System.out.println("This is Great!!"));不接受參數且返回 void 。 這恰恰是 Runnable 接口中 run 方法的簽名。
小測一把
以下哪些是使用Lambda表達式的有效方式?
(1) execute(() -> {});public void execute(Runnable r){r.run(); } (2) public Callable<String> fetch() {return () -> "Tricky example ;-)"; } (3) Predicate<Apple> p = (Apple a) -> a.getWeight();答案:只有1和2是有效的。
-
第一個例子有效,是因為Lambda () -> {} 具有簽名 () -> void ,這和 Runnable 中的抽象方法 run 的簽名相匹配。請注意,此代碼運行后什么都不會做,因為Lambda是空的
-
第二個例子也是有效的。事實上, fetch 方法的返回類型是 Callable<String> 。
Callable<String> 基本上就定義了一個方法,簽名是 () -> String ,其中 T 被 String 代替
了。因為Lambda () -> "Trickyexample;-)" 的簽名是 () -> String ,所以在這個上下文
中可以使用Lambda。 -
第三個例子無效,因為Lambda表達式 (Apple a) -> a.getWeight() 的簽名是
(Apple) -> Integer ,這和 Predicate<Apple>:(Apple) -> boolean 中定義的 test 方法的簽名不同。
總結
以上是生活随笔為你收集整理的Java 8 - 02 Lambda Expression的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java 8 - 01 优雅编程 lam
- 下一篇: Java 8 - 03 Lambda 函