摘要:表達式簡介表達式是一個匿名函數(shù)對于而言并不很準確,但這里我們不糾結這個問題。如果表達式的正文有一條以上的語句必須包含在大括號代碼塊中,且表達式的返回值類型要與匿名函數(shù)的返回類型相同。
1. 引言版權聲明:本文由吳仙杰創(chuàng)作整理,轉載請注明出處:https://segmentfault.com/a/1190000009186509
在 Java 8 以前,若我們想要把某些功能傳遞給某些方法,總要去寫匿名類。以前注冊事件監(jiān)聽器的寫法與下面的示例代碼就很像:
manager.addScheduleListener(new ScheduleListener() { @Override public void onSchedule(ScheduleEvent e) { // Event listener implementation goes here... } });
這里我們添加了一些自定義代碼到 Schedule 監(jiān)聽器中,需要先定義匿名內(nèi)部類,然后傳遞一些功能到 onSchedule 方法中。
正是 Java 在作為參數(shù)傳遞普通方法或功能的限制,Java 8 增加了一個全新語言級別的功能,稱為 Lambda 表達式。
2. 為什么 Java 需要 Lambda 表達式Java 是面向對象語言,除了原始數(shù)據(jù)類型之處,Java 中的所有內(nèi)容都是一個對象。而在函數(shù)式語言中,我們只需要給函數(shù)分配變量,并將這個函數(shù)作為參數(shù)傳遞給其它函數(shù)就可實現(xiàn)特定的功能。JavaScript 就是功能編程語言的典范(閉包)。
Lambda 表達式的加入,使得 Java 擁有了函數(shù)式編程的能力。在其它語言中,Lambda 表達式的類型是一個函數(shù);但在 Java 中,Lambda 表達式被表示為對象,因此它們必須綁定到被稱為功能接口的特定對象類型。
3. Lambda 表達式簡介Lambda 表達式是一個匿名函數(shù)(對于 Java 而言并不很準確,但這里我們不糾結這個問題)。簡單來說,這是一種沒有聲明的方法,即沒有訪問修飾符,返回值聲明和名稱。
在僅使用一次方法的地方特別有用,方法定義很短。它為我們節(jié)省了,如包含類聲明和編寫多帶帶方法的工作。
Java 中的 Lambda 表達式通常使用語法是 (argument) -> (body),比如:
(arg1, arg2...) -> { body } (type1 arg1, type2 arg2...) -> { body }
以下是 Lambda 表達式的一些示例:
(int a, int b) -> { return a + b; } () -> System.out.println("Hello World"); (String s) -> { System.out.println(s); } () -> 42 () -> { return 3.1415 };3.1 Lambda 表達式的結構
Lambda 表達式的結構:
Lambda 表達式可以具有零個,一個或多個參數(shù)。
可以顯式聲明參數(shù)的類型,也可以由編譯器自動從上下文推斷參數(shù)的類型。例如 (int a) 與剛才相同 (a)。
參數(shù)用小括號括起來,用逗號分隔。例如 (a, b) 或 (int a, int b) 或 (String a, int b, float c)。
空括號用于表示一組空的參數(shù)。例如 () -> 42。
當有且僅有一個參數(shù)時,如果不顯式指明類型,則不必使用小括號。例如 a -> return a*a。
Lambda 表達式的正文可以包含零條,一條或多條語句。
如果 Lambda 表達式的正文只有一條語句,則大括號可不用寫,且表達式的返回值類型要與匿名函數(shù)的返回類型相同。
如果 Lambda 表達式的正文有一條以上的語句必須包含在大括號(代碼塊)中,且表達式的返回值類型要與匿名函數(shù)的返回類型相同。
4. 方法引用 4.1 從 Lambda 表達式到雙冒號操作符使用 Lambda 表達式,我們已經(jīng)看到代碼可以變得非常簡潔。
例如,要創(chuàng)建一個比較器,以下語法就足夠了
Comparator c = (Person p1, Person p2) -> p1.getAge().compareTo(p2.getAge());
然后,使用類型推斷:
Comparator c = (p1, p2) -> p1.getAge().compareTo(p2.getAge());
但是,我們可以使上面的代碼更具表現(xiàn)力和可讀性嗎?我們來看一下:
Comparator c = Comparator.comparing(Person::getAge);
使用 :: 運算符作為 Lambda 調用特定方法的縮寫,并且擁有更好的可讀性。
4.2 使用方式雙冒號(::)操作符是 Java 中的方法引用。 當們使用一個方法的引用時,目標引用放在 :: 之前,目標引用提供的方法名稱放在 :: 之后,即 目標引用::方法。比如:
Person::getAge;
在 Person 類中定義的方法 getAge 的方法引用。
然后我們可以使用 Function 對象進行操作:
// 獲取 getAge 方法的 Function 對象 FunctiongetAge = Person::getAge; // 傳參數(shù)調用 getAge 方法 Integer age = getAge.apply(p);
我們引用 getAge,然后將其應用于正確的參數(shù)。
目標引用的參數(shù)類型是 Function
在 Java 中,功能接口(Functional interface)指只有一個抽象方法的接口。
java.lang.Runnable 是一個功能接口,在 Runnable 中只有一個方法的聲明 void run()。我們使用匿名內(nèi)部類實例化功能接口的對象,而使用 Lambda 表達式,可以簡化寫法。
每個 Lambda 表達式都可以隱式地分配給功能接口。例如,我們可以從 Lambda 表達式創(chuàng)建 Runnable 接口的引用,如下所示:
Runnable r = () -> System.out.println("hello world");
當我們不指定功能接口時,這種類型的轉換會被編譯器自動處理。例如:
new Thread( () -> System.out.println("hello world") ).start();
在上面的代碼中,編譯器會自動推斷,Lambda 表達式可以從 Thread 類的構造函數(shù)簽名(public Thread(Runnable r) { })轉換為 Runnable 接口。
@FunctionalInterface 是在 Java 8 中添加的一個新注解,用于指示接口類型,聲明接口為 Java 語言規(guī)范定義的功能接口。Java 8 還聲明了 Lambda 表達式可以使用的功能接口的數(shù)量。當您注釋的接口不是有效的功能接口時, @FunctionalInterface 會產(chǎn)生編譯器級錯誤。
以下是自定義功能接口的示例:
package com.wuxianjiezh.demo.lambda; @FunctionalInterface public interface WorkerInterface { public void doSomeWork(); }
正如其定義所述,功能接口只能有一個抽象方法。如果我們嘗試在其中添加一個抽象方法,則會拋出編譯時錯誤。例如:
package com.wuxianjiezh.demo.lambda; @FunctionalInterface public interface WorkerInterface { public void doWork(); public void doMoreWork(); }
錯誤:
Error:(3, 1) java: 意外的 @FunctionalInterface 注釋 com.wuxianjiezh.demo.lambda.WorkerInterface 不是函數(shù)接口 在 接口 com.wuxianjiezh.demo.lambda.WorkerInterface 中找到多個非覆蓋抽象方法
一旦定義了功能接口,我們就可以利用 Lambda 表達式調用。例如:
package com.wuxianjiezh.demo.lambda; @FunctionalInterface public interface WorkerInterface { public void doWork(); } class WorkTest { public static void main(String[] args) { // 通過匿名內(nèi)部類調用 WorkerInterface work = new WorkerInterface() { @Override public void doWork() { System.out.println("通過匿名內(nèi)部類調用"); } }; work.doWork(); // 通過 Lambda 表達式調用 // Lambda 表達式實際上是一個對象。 // 我們可以將 Lambda 表達式賦值給一個變量,就可像其它對象一樣調用。 work = ()-> System.out.println("通過 Lambda 表達式調用"); work.doWork(); } }
運行結果:
通過匿名內(nèi)部類調用 通過 Lambda 表達式調用6. Lambda 表達式的例子 6.1 線程初始化
線程可以初始化如下:
// Old way new Thread(new Runnable() { @Override public void run() { System.out.println("Hello world"); } }).start(); // New way new Thread( () -> System.out.println("Hello world") ).start();6.2 事件處理
事件處理可以用 Java 8 使用 Lambda 表達式來完成。以下代碼顯示了將 ActionListener 添加到 UI 組件的新舊方式:
// Old way button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("Hello world"); } }); // New way button.addActionListener( (e) -> { System.out.println("Hello world"); });6.3 遍例輸出(方法引用)
輸出給定數(shù)組的所有元素的簡單代碼。請注意,還有一種使用 Lambda 表達式的方式。
// old way List6.4 邏輯操作list = Arrays.asList(1, 2, 3, 4, 5, 6, 7); for (Integer n : list) { System.out.println(n); } // 使用 -> 的 Lambda 表達式 list.forEach(n -> System.out.println(n)); // 使用 :: 的 Lambda 表達式 list.forEach(System.out::println);
輸出通過邏輯判斷的數(shù)據(jù)。
package com.wuxianjiezh.demo.lambda; import java.util.Arrays; import java.util.List; import java.util.function.Predicate; public class Main { public static void main(String[] args) { Listlist = Arrays.asList(1, 2, 3, 4, 5, 6, 7); System.out.print("輸出所有數(shù)字:"); evaluate(list, (n) -> true); System.out.print("不輸出:"); evaluate(list, (n) -> false); System.out.print("輸出偶數(shù):"); evaluate(list, (n) -> n % 2 == 0); System.out.print("輸出奇數(shù):"); evaluate(list, (n) -> n % 2 == 1); System.out.print("輸出大于 5 的數(shù)字:"); evaluate(list, (n) -> n > 5); } public static void evaluate(List list, Predicate predicate) { for (Integer n : list) { if (predicate.test(n)) { System.out.print(n + " "); } } System.out.println(); } }
運行結果:
輸出所有數(shù)字:1 2 3 4 5 6 7 不輸出: 輸出偶數(shù):2 4 6 輸出奇數(shù):1 3 5 7 輸出大于 5 的數(shù)字:6 76.4 Stream API 示例
java.util.stream.Stream接口 和 Lambda 表達式一樣,都是 Java 8 新引入的。所有 Stream 的操作必須以 Lambda 表達式為參數(shù)。Stream 接口中帶有大量有用的方法,比如 map() 的作用就是將 input Stream 的每個元素,映射成output Stream 的另外一個元素。
下面的例子,我們將 Lambda 表達式 x -> x*x 傳遞給 map() 方法,將其應用于流的所有元素。之后,我們使用 forEach 打印列表的所有元素。
// old way Listlist = Arrays.asList(1,2,3,4,5,6,7); for(Integer n : list) { int x = n * n; System.out.println(x); } // new way List list = Arrays.asList(1,2,3,4,5,6,7); list.stream().map((x) -> x*x).forEach(System.out::println);
下面的示例中,我們給定一個列表,然后求列表中每個元素的平方和。這個例子中,我們使用了 reduce() 方法,這個方法的主要作用是把 Stream 元素組合起來。
// old way List7. Lambda 表達式和匿名類之間的區(qū)別list = Arrays.asList(1,2,3,4,5,6,7); int sum = 0; for(Integer n : list) { int x = n * n; sum = sum + x; } System.out.println(sum); // new way List list = Arrays.asList(1,2,3,4,5,6,7); int sum = list.stream().map(x -> x*x).reduce((x,y) -> x + y).get(); System.out.println(sum);
this 關鍵字。對于匿名類 this 關鍵字解析為匿名類,而對于 Lambda 表達式,this 關鍵字解析為包含寫入 Lambda 的類。
編譯方式。Java 編譯器編譯 Lambda 表達式時,會將其轉換為類的私有方法,再進行動態(tài)綁定。
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/69911.html
摘要:實際上方法引用是表達式的一種語法糖。小結本篇全面介紹了方法引用的四種使用方式,且每種方式都有對應一個示例來幫助大家理解。 上一篇我們詳細介紹了Optional類用來避免空指針問題,本篇我們?nèi)媪私庖幌翵ava8中的方法引用特性。方法引用是lambda表達式的一種特殊形式,如果正好有某個方法滿足一個lambda表達式的形式,那就可以將這個lambda表達式用方法引用的方式表示,但是如果這...
摘要:局部變量表達式的方法體與嵌套代碼塊有著相同的作用域。在表達式中不允許聲明一個與局部變量同名的參數(shù)或者局部變量。不可變的約束只作用在局部變量上,如果是一個實例變量或者閉合類的靜態(tài)變量,那么不會有任何錯誤被報告出來即使結果同樣未定義。 完整的Java學習的路線圖可以參考:我的編程之路--知識管理與知識體系 Lambda&Closures Java8 Lambda表達式10個示例 閉包一般指...
摘要:初體驗下面進入本文的正題表達式。接下來展示表達式和其好基友的配合。吐槽一下方法引用表面上看起來方法引用和構造器引用進一步簡化了表達式的書寫,但是個人覺得這方面沒有的下劃線語法更加通用。 感謝同事【天錦】的投稿。投稿請聯(lián)系 tengfei@ifeve.com 本文主要記錄自己學習Java8的歷程,方便大家一起探討和自己的備忘。因為本人也是剛剛開始學習Java8,所以文中肯定有錯誤和理解偏...
小編寫這篇文章的話,主要是給大家做出一個解答,解答一些Python常見問題,比如關于編程函數(shù)的一些問題,哪些函數(shù)編程是最受用的呢?下面就給大家詳細介紹一下?! 『侠淼氖褂肞ython這門工具,能夠大大的提高其工作效率,起到事半功倍的作用?! ?.Map函數(shù) map函數(shù)可以使用另外一個函數(shù)轉換整個可迭代對象的函數(shù),包括將字符串轉換為數(shù)字、數(shù)字的四舍五入等等。 之所以使用map函數(shù)來完成這些事...
以下是Java技術棧微信公眾號發(fā)布的關于 Java 的技術干貨,從以下幾個方面匯總。 Java 基礎篇 Java 集合篇 Java 多線程篇 Java JVM篇 Java 進階篇 Java 新特性篇 Java 工具篇 Java 書籍篇 Java基礎篇 8張圖帶你輕松溫習 Java 知識 Java父類強制轉換子類原則 一張圖搞清楚 Java 異常機制 通用唯一標識碼UUID的介紹及使用 字符串...
閱讀 2303·2021-11-25 09:43
閱讀 2941·2019-08-30 15:52
閱讀 1890·2019-08-30 15:44
閱讀 982·2019-08-30 10:58
閱讀 758·2019-08-29 18:43
閱讀 3215·2019-08-29 18:36
閱讀 2316·2019-08-29 17:02
閱讀 1457·2019-08-29 17:01