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