為什麼必須是final的呢?
一個謎團
如果你用過類似guava這種“偽函數語言程式設計”風格的library的話,那下面這種風格的程式碼對你來說應該不陌生:
1 2 3 4 5 6 7 8 9 |
|
這段程式碼對一個字串的list進行過濾,從中找出長度為4的字串。看起來很是平常,沒什麼特別的。
但是,宣告expectedLength時用的那個final看起來有點扎眼,把它去掉試試:
error: local variable expectedLength is accessed from within inner class; needs to be declared final
結果Java編譯器給出瞭如上的錯誤,看起來匿名內部類只能夠訪問final的區域性變數。但是,為什麼呢?其他的語言也有類似的規定嗎?
在開始用其他語言做實驗之前我們先把問題簡化一下,不要再帶著guava了,我們去除掉噪音,把問題歸結為:
為什麼Java中的匿名內部類只可以訪問final的區域性變數呢?其他語言中的匿名函式也有類似的限制嗎?
Scala中有類似的規定嗎?
1 2 3 4 5 6 7 8 9 10 11 12 |
|
上面的Scala程式碼是合法的,number變數是宣告為var的,不是val(類似於Java中的final)。而且在匿名函式中可以修改number的值。
看來Scala中沒有類似的規定。
C#中有類似的規定嗎?
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
這段C#程式碼也是合法的,number這個區域性變數在lambda表示式內外都可以訪問和賦值。
看來C#中也沒有類似的規定。
分析謎團
三門語言中只有Java有這種限制,那我們分析一下吧。先來看一下Java中的匿名內部類是如何實現的:
先定義一個介面:
1 2 3 |
|
然後建立這個介面的匿名子類:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
這個匿名子類會被編譯成一個單獨的類,反編譯的結果是這樣的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|