日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

lambda表达式或者匿名函数中为什么要求外部变量为final

發(fā)布時(shí)間:2025/3/12 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 lambda表达式或者匿名函数中为什么要求外部变量为final 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1、參考博客

  • 關(guān)于Lambda表達(dá)式里面修改外部變量問題
  • JDK8之前,匿名內(nèi)部類訪問的局部變量為什么必須要用final修飾
  • 2、匿名內(nèi)部類

    在jdk7之前,匿名內(nèi)部類訪問外部類的局部變量時(shí),那么這個(gè)局部變量必須用final修飾符修飾,如下圖1所示。jdk8則不需要,但是我們?cè)谑褂眠@個(gè)局部變量時(shí),無法改變局部變量的值,否則編譯會(huì)報(bào)錯(cuò)。

    這個(gè)特點(diǎn)為什么要求是final類的呢?
    因?yàn)樵趈ava設(shè)計(jì)之初為了保護(hù)數(shù)據(jù)的一致性而規(guī)定的。對(duì)引用變量來說是引用地址的一致性,對(duì)基本類型來說就是值的一致性。

    注意:在JDK8之后,匿名內(nèi)部類引用外部變量時(shí)雖然不用顯式的用final修飾,但是這個(gè)外部變量必須和final一樣,不能被修改(這是一個(gè)坑)。解決方案:可以通過定義一個(gè)相同類型的變量b,然后將該外部變量賦值給b,匿名內(nèi)部類引用b就行了,然后就可以繼續(xù)修改外部變量。

    3、lambda表達(dá)式

    這是因?yàn)?#xff1a;Java會(huì)將result的值作為參數(shù)傳遞給Lambda表達(dá)式,為Lambda表達(dá)式建立一個(gè)副本,它的代碼訪問的是這個(gè)副本,而不是外部聲明result變量。可能很多同學(xué)會(huì)問為什么非要建立副本呢,直接訪問外部的result變量得多方便呢。答案是:這是不可能滴,因?yàn)閞esult定義在棧中,當(dāng)Lambda表達(dá)式被執(zhí)行的時(shí)候,result可能已經(jīng)被釋放掉了。

    當(dāng)然啦,你要是一定要在Lambda表達(dá)式里面修改外部變量的值也是可以的,可以將變量
    定義為實(shí)例變量或者將變量定義為數(shù)組。

    ① 情形一

    如下圖所示:在使用外部類的局部變量時(shí),如果試圖修改值,就會(huì)編譯報(bào)錯(cuò)。

    ② 情形二

    如果局部變量是對(duì)象類型,則對(duì)象的引用地址不可改變,如下圖所示。但是如果在局部變量中修改對(duì)象是沒有問題的(第二篇博客有詳細(xì)解釋)。

    ③ 情形三

    在項(xiàng)目中,我遇到過這種情況:有兩個(gè)集合,我先對(duì)一個(gè)集合進(jìn)行遍歷,在這里面又需要對(duì)另外一個(gè)集合遍歷。當(dāng)時(shí)出現(xiàn)了編譯報(bào)錯(cuò):variable used in lambda expression should be final or effectively final(lambda表達(dá)式中使用的變量應(yīng)該是final或effective final),位置在下圖的紅框內(nèi)。如下圖所示。但是下圖當(dāng)我工作之余究其緣由調(diào)試時(shí)的demo,此時(shí)編譯又不報(bào)錯(cuò)了。我能力有限,暫時(shí)還不清楚問題出現(xiàn)的原因。當(dāng)時(shí)我的解決方案有兩個(gè):一個(gè)是在將lambda表達(dá)式換成了for下標(biāo)遍歷,就OK了。另一種方案是創(chuàng)建一個(gè)報(bào)錯(cuò)的同類型新對(duì)象,將list1的引用賦值給新對(duì)象,編譯也不報(bào)錯(cuò)了(如下下圖所示)。

    為什么lambda表達(dá)式使用的局部變量要是final的

    為什么 Lambda 表達(dá)式(匿名類) 不能訪問非 final 類型的局部變量呢?

    之前我一直認(rèn)為是由于Lambda 表達(dá)式(匿名類) 會(huì)在另一個(gè)線程中執(zhí)行,實(shí)例變量存在堆中,而局部變量是在棧上分配,如果在線程中要直接訪問一個(gè)局部變量,可能線程執(zhí)行時(shí)該局部變量已經(jīng)被銷毀了。

    但是有人提出這個(gè)原因存在問題,說他們是在同一個(gè)線程中運(yùn)行的,大兄弟提供的驗(yàn)證代碼如下:

    public class Java8Tester {public static void main(String[] args) {String str = "Hello, ";Person person = name -> {System.out.println(str + name + " " + Thread.currentThread().getId());System.out.println("匿名類" + " " + Thread.currentThread().getId());};System.out.println("主方法" + " " + Thread.currentThread().getId());person.say("JC");}interface Person {void say(String name);} }

    運(yùn)行之后的結(jié)果顯示確實(shí)都是main線程運(yùn)行的,結(jié)果如下:

    我又去看了一些其他的博客,有人說是Java為了防止數(shù)據(jù)不同步而規(guī)定的,也就是為了防止在lambda表達(dá)式內(nèi)使用的外層局部變量被外層代碼修改之后內(nèi)部無法同步這個(gè)修改。想看原貼可以點(diǎn)我

    我覺得上面超鏈接中講述的原因是比較好的,如果其他同學(xué)有不同意見歡迎一起交流一起進(jìn)步。

    Java 8 的 Lambda 可以捕獲什么變量呢?

    (1). 捕獲實(shí)例變量或靜態(tài)變量是沒有限制的 (可認(rèn)為是通過 final 類型的局部變量 this 來引用前兩者);

    (2). 捕獲的局部變量必須顯式的聲明為 final 或?qū)嶋H效果的的 final 類型
    注意(敲黑板):如果在Lambda表達(dá)式中使用局部變量,即使我們沒有聲明成final類型的,編譯器也會(huì)幫助我們將他們聲明成final的,所以如果重新賦值會(huì)出錯(cuò)。

    如下:

    結(jié)果如下:

    此時(shí)程序不會(huì)報(bào)錯(cuò),雖然我們沒有使用final修飾變量b,但是Lambda表達(dá)式中也能使用,這是因?yàn)榫幾g器幫我們自動(dòng)聲明成final的。

    眾所周知,final類型的變量不能重新賦值,來驗(yàn)證下是不是編譯器真的幫我們聲明成了final,如下:

    報(bào)錯(cuò):

    那么是什么時(shí)候幫我們聲明成final呢?答案是在創(chuàng)建變量的時(shí)候直接聲明成final的,測試如下:

    總結(jié):在Lambda表達(dá)式中可以捕獲靜態(tài)變量和實(shí)例變量,但是如果想要捕獲局部變量的時(shí)候就需要聲明成final的,即使我們不主動(dòng)聲明,編譯器也會(huì)為我們自動(dòng)聲明成final的,不能再重新賦值。也就是Lambda表達(dá)式中訪問的局部變量(隱式被聲明為final的)是可讀不可寫的,但是Lambda表達(dá)式中訪問的實(shí)例變量和靜態(tài)變量是可讀可寫的。




    為什么會(huì)這樣?

    在Java中l(wèi)ambda表達(dá)式是匿名類語法上的進(jìn)一步簡化,它的本質(zhì)其實(shí)還是調(diào)用對(duì)象的方法。lambda表達(dá)式會(huì)以內(nèi)聯(lián)的形式創(chuàng)建一個(gè)函數(shù)式接口的實(shí)例,保存在堆中,而局部變量則保存在棧中,而在Java中方法調(diào)用是值傳遞的(特別聲明java中都是按值傳遞的!!!),所以在lambda表達(dá)式中對(duì)變量的操作都是基于原變量的副本,不會(huì)影響到原變量的值。那假如沒有要求lambda表達(dá)式外部變量為final修飾,那么就會(huì)誤以為外部變量的值能夠在lambda表達(dá)式中被改變,而這實(shí)際是不可能的,所以要求外部變量為final。
    我們知道局部變量隨著方法的調(diào)用會(huì)被壓入棧中,當(dāng)方法調(diào)用結(jié)束時(shí),出棧,這些局部變量全部死亡。而函數(shù)式接口實(shí)例對(duì)象生命周期和其他類對(duì)象是一樣的,從創(chuàng)建一個(gè)實(shí)例對(duì)象開始,系統(tǒng)就會(huì)為該對(duì)象分配內(nèi)存,直到?jīng)]有引用變量指向分配給該對(duì)象得內(nèi)存,它被GC垃圾回收,所以很可能出現(xiàn)的一種情況就是:方法已調(diào)用結(jié)束,局部變量已死亡,但實(shí)例對(duì)象的對(duì)象仍然活著。也就是說如果這個(gè)時(shí)候仍然有引誘變量指向lambda表達(dá)式在堆中所創(chuàng)建的實(shí)例,但是lambda表達(dá)式中的局部變量已經(jīng)死亡,豈不是出了問題,這在java中是不允許的,在我還需要你的時(shí)候你的局部變量是不可以死亡的。

    以上就是我對(duì)這個(gè)點(diǎn)的理解,小伙伴們有什么不同的見解或者文章中有什么錯(cuò)誤的地方,歡迎大家在里評(píng)論區(qū)留言

    總結(jié)

    以上是生活随笔為你收集整理的lambda表达式或者匿名函数中为什么要求外部变量为final的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。