摘要:局部變量表達式的方法體與嵌套代碼塊有著相同的作用域。在表達式中不允許聲明一個與局部變量同名的參數或者局部變量。不可變的約束只作用在局部變量上,如果是一個實例變量或者閉合類的靜態變量,那么不會有任何錯誤被報告出來即使結果同樣未定義。
完整的Java學習的路線圖可以參考:我的編程之路--知識管理與知識體系
Lambda&ClosuresJava8 Lambda表達式10個示例
閉包一般指存在自由變量的代碼塊,它與對象類似,都是用來描述一段代碼與其環境的關系。在Java中,Lambda表達式就是閉包。事實上,內部類一直都是閉包,而Java8中為閉包賦予了更吸引人的語法。一個Lambda表達式包括三個部分:
一段代碼
參數
自由變量的值,這里的“自由”指的是那些不是參數并且沒有在代碼中定義的變量。
Lambda表達式本身是構造了一個繼承自某個函數式接口的子類,所以可以用父類指針指向它。Java中本質上閉包中是采用的值捕獲,即不可以在閉包中使用可變對象。但是它實際上是允許捕獲事實上不變量,譬如不可變的ArrayList,只是指針指向不可變罷了。雖然實現用的是值捕獲,但效果看起來跟引用捕獲一樣;就算以后的Java擴展到允許通用的(對可變變量的)引用捕獲,也不會跟已有的代碼發生不兼容。
Java中最常見的閉包的使用如下所示:
Runnable task = () -> { // do something }; Comparator方法引用cmp = (s1, s2) -> { return Integer.compare(s1.length(), s2.length()); };
有時候Lambda表達式的代碼就只是一個簡單的方法調用而已,遇到這種情況,Lambda表達式還可以進一步簡化為 方法引用(Method References) 。一共有四種形式的方法引用。
(1)靜態方法引用
Listints = Arrays.asList(1, 2, 3); ints.sort(Integer::compare);
(2)某個特定對象的實例方法
例如前面那個遍歷并打印每一個word的例子可以寫成這樣:
words.forEach(System.out::println);
(3)某個類的實例方法
例如:
words.stream().map(word -> word.length()); // lambda words.stream().map(String::length); // method reference
(4)構造函數引用
// lambda words.stream().map(word -> { return new StringBuilder(word); }); // constructor reference words.stream().map(StringBuilder::new);Variable Scope(變量作用域)
在Lambda中,變量的作用域與訪問操作主要遵循以下規則:
本地變量(Local Variable)可以訪問但是不可以修改。
類成員變量與靜態變量可以被讀寫,即閉包中的this實際指向的是創建該Lambda表達式的方法的this參數。
函數式接口的默認方法不可以在Lambda表達式中被訪問。
(1)局部變量
lambda表達式的方法體與嵌套代碼塊有著相同的作用域。因此它也適用同樣的命名沖突和屏蔽規則。在lambda表達式中不允許聲明一個與局部變量同名的參數或者局部變量。
Path first = Paths.get("/usr/bin"); Comparatorcomp = (first,second) -> Integer.compare(first.length(),second.length()); //錯誤,變量first已經定義了
在一個方法里,你不能有兩個同名的局部變量,因此,你也不能在lambda表達式中引入這樣的變量。在下一個示例中,lambda表達式有兩個自由變量,text和count。數據結構表示lambda表達式必須存儲這兩個變量的值,即“Hello”和20。我們可以說,這些值已經被lambda表達式捕獲了(這是一個技術實現的細節。例如,你可以將一個lambda表達式轉換為一個只含一個方法的對象,這樣自由變量的值就會被復制到該對象的實例變量中)。
package java8test; public class T1 { public static void main(String[] args) { repeatMessage("Hello", 20); } public static void repeatMessage(String text,int count){ Runnable r = () -> { for(int i = 0; i < count; i++){ System.out.println(text); Thread.yield(); } }; new Thread(r).start(); } }
(2)this
當你在lambda表達式中使用this關鍵字,你會引用創建該lambda表達式的方法的this參數,以下面的代碼為例:
public class Application{ public void doWork(){ Runnable runner = () -> {....;System.out.println(this.toString());......}; } }
表達式this.toString()會調用Application對象的toString()方法,而不是Runnable實例的toString()方法。在lambda表達式中使用this,與在其他地方使用this沒有什么不同。lambda表達式的作用域被嵌套在doWork()方法中,并且無論this位于方法的何處,其意義都是一樣的。
(3)引用的變量不可更改
Lambda表達式可以捕獲閉合作用域中的變量值。在Java中,為了確保被捕獲的值是被良好定義的,需要遵守一個重要的約束。在lambda表達式中,被引用的變量的值不可以被更改。例如,下面這個表達式是不合法的:
public static void repeatMessage(String text,int count){ Runnable r = () -> { while(count > 0){ count--; //錯誤,不能更改已捕獲變量的值 System.out.println(text); Thread.yield(); } }; new Thread(r).start(); }
做出這個約束是有原因的。更改lambda表達式中的變量不是線程安全的。假設有一系列并發的任務,每個線程都會更新一個共享的計數器。
int matches = 0; for(Path p : files) new Thread(() -> {if(p中包含某些屬性) matches++;}).start(); //非法更改matches的值
如果這段代碼是合法的,那么會引起十分糟糕的結果。自增操作matches++不是原子操作,如果多個線程并發執行該自增操作,天曉得會發生什么。不要指望編譯器會捕獲所有并發訪問錯誤。不可變的約束只作用在局部變量上,如果matches是一個實例變量或者閉合類的靜態變量,那么不會有任何錯誤被報告出來即使結果同樣未定義。同樣,改變一個共享對象也是完全合法的,即使這樣并不恰當。例如:
Listmatches = new ArrayList<>(); for(Path p: files) //你可以改變matches的值,但是在多線程下是不安全的 new Thread(() -> {if(p中包含某些屬性) matches.add(p);}).start();
注意matches是“有效final”的(一個有效的final變量被初始化后,就永遠不會再被賦一個新值的變量)。在我們的示例中,matches總是引用同一個ArrayList對象,但是,這個對象是可變的,因此是線程不安全的 。如果多個線程同時調用add方法,結果將無法預測。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/65627.html
摘要:哪吒社區技能樹打卡打卡貼函數式接口簡介領域優質創作者哪吒公眾號作者架構師奮斗者掃描主頁左側二維碼,加入群聊,一起學習一起進步歡迎點贊收藏留言前情提要無意間聽到領導們的談話,現在公司的現狀是碼農太多,但能獨立帶隊的人太少,簡而言之,不缺干 ? 哪吒社區Java技能樹打卡?【打卡貼 day2...
摘要:實踐很簡單的一個類,點開它的源代碼,其中所有的方法都是與相關聯的。從而很好地避免了空指針異常。方法,如果存在,返回包含的值,否則拋出異常。隨便點開一個方法,都會在第一行為不該為的參數進行判斷。 問題描述 在大熱的Spring Boot 2.0中,在將原來的泛型改為了Optional,旨在讓我們的代碼更簡潔。 showImg(https://segmentfault.com/img/bV...
摘要:判斷奇數是迭代器會根據提供的函數對指定序列做映射語法可以對可迭代對象中的每一個元素進行映射。 python內置庫詳解 1、引言2、內置庫詳解2.1 數據相關2.1...
摘要:函數式接口進階與默認方法詳解上一篇我們快速的借助示例演示了的簡單應用,體會到了使用對集合處理的便捷和其與函數式接口密不可分的關系,所以為了更高效的使用,有必要更熟練的掌握函數式接口。 Java8-5-函數式接口進階與默認方法詳解上一篇我們快速的借助示例演示了stream api的簡單應用,體會到了使用stream api對集合處理的便捷和其與函數式接口密不可分的關系,所以為了更高效的使...
閱讀 1816·2023-04-26 01:44
閱讀 1222·2021-11-12 10:34
閱讀 1614·2021-09-09 09:33
閱讀 1743·2019-08-30 15:44
閱讀 2904·2019-08-30 13:49
閱讀 2200·2019-08-29 15:26
閱讀 954·2019-08-26 13:30
閱讀 1422·2019-08-23 18:15