爲何lambda內使用的外層局部變量必須是final

直接給出答案

開門見山的說啊,這是java爲了防止數據不一樣步而規定的,也就是防止你在lambda內使用的外層局部變量被外層代碼修改了,可是lambda內部沒法同步這個修改java

爲何lambda沒法感知外層局部變量的變化

那麼問題就轉移到,爲何你在外層代碼修改了變量,lambda內部會感知不到呢?這個問題就要回到lambda表達式的本質了函數

lambda表達式的本質

lambda表達式的本質是什麼?也就是匿名內部類,或者再精確一點,就是一個函數式接口的實現的實例嘛code

外層局部變量是如何傳入lambda的

那再想一下,你把外層局部變量拿到這個實例去使用,這個變量是如何傳進去的?是經過普通方法傳參嗎?固然不是,你參數列表又不能改變,答案是構造器嘛。lambda表達式實例化的時候,編譯器會建立一個新的class文件(想一下你是否是在工程編譯以後見到過相似於Main$1.class的文件),該文件就是lambda實例化的類的字節碼文件,在該文件中,編譯器幫咱們建立了一個構造器,該構造器的入參中就包含了你要使用的外層局部變量,因此外層局部變量就經過lambda的構造器傳入實例內部供其使用對象

值傳遞和引用傳遞

那麼這個時候就很好理解,爲何lambda內使用的外層局部變量必須是final了。你想嘛,既然你是經過構造器傳參,構造器也是方法,若是這個變量是基本類型,那確定是值傳遞嘛,也就是傳副本兒,那既然是副本,你在外層代碼修改了這個變量,那lambda內確定就沒法感知了嘛。不過啊,若是是引用傳遞,其實就不存在這個問題了,由於final關鍵字只是維護引用的地址,而不會維護引用的對象內部的屬性值接口

lambda只是聲明,和聲明變量是同樣的道理

最後要說的一點就是,不管是lambda仍是匿名內部類,在寫lambda表達式的時候,是不會直接去執行這個lambda表達式的,lambda只是一種聲明,和聲明變量同樣,你聲明一個int x;僅僅是聲明,可能在不少行代碼以後纔去調用這個lambda表達式的執行,例如:編譯器

Thread thread = new Thread(() -> System.out.println("call"));
System.out.println("main call");
thread.start();

執行第1行代碼,聲明瞭lambda表達式,但此時不會執行lambda表達式
執行第3行代碼,纔會去執行lambda同步

總結

好了如今就很清晰了,假如你在第1行代碼的lambda內使用了一個外層局部變量,此時其值爲10,而後在第2行代碼改變了這個變量爲12,最後第3行代碼去執行lambda,但其內部這個變量的值仍然爲10,這就形成了數據不一樣步的問題編譯

相關文章
相關標籤/搜索