今天群里討論java
的lambda
實現.
后來不斷衍生談到了為什么lambda和匿名內部類只能使用語義final
的外部變量.
最開始以為是java的lambda實現問題,編譯期魔法會把外部引用作為參數傳入所以在內部變化也影響不了下次調用的值,所以就干脆final了,如果用類的屬性來保管這個變量就可以了.
In [64]: def outer(a:int):
...: def inner():
...: nonlocal a
...: a = a + a
...: return a
...: return inner
...:
In [65]: x = outer(1)
In [66]: x
Out[66]: <function __main__.outer.<locals>.inner>
In [67]: x()
Out[67]: 2
In [68]: x()
Out[68]: 4
In [69]: x()
Out[69]: 8
舉例就是這種情況
lambda用參數傳入外部int,如果在方法里修改了,下次調用這個lambda依舊是以前的值.
后來又去看了眼匿名內部類的實現
public class InnerTest {
public static void main(String[] args) {
String name = "123";
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println(name);
}
};
r.run();
}
}
InnerTest$1(java.lang.String val$name);
0 aload_0 [this]
1 aload_1 [val$name]
2 putfield InnerTest$1.val$name : java.lang.String [12]
5 aload_0 [this]
6 invokespecial java.lang.Object() [14]
9 return
Line numbers:
[pc: 0, line: 1]
[pc: 5, line: 11]
Local variable table:
[pc: 0, pc: 10] local: this index: 0 type: new InnerTest(){}
Method Parameters:
final synthetic val$name
構造方法字節碼明明都存下來了呀...為什么那時候就要求final
了
查閱了一下,發現這樣有個很大的問題.
這個外部變量在匿名內部類初始化之后就被固定了下來,之后他如果被重新賦值(引用類型內部狀態修改除外),就會出現內部無法看見外部,外部也無法看見內部的問題... ...
舉個簡單的例子:
In [101]: def outer(x):
...: def inner():
...: nonlocal x
...: x = x+1
...: print("inner"+str(x))
...: inner()
...: print("outer"+str(x))
...: x = x+1
...: inner()
...:
In [102]: outer(1)
inner2(inner內部+1)
outer2(外部看到這個變化)
inner4(外部+1 內部+1)
這段在py下能正常工作的代碼,如果java沒有final
限制的話,就會變成
inner2(inner內部+1)
outer1(外部看不到inner變化)
inner3(inner內部+1)
所以這個約定和閉包實現沒關系...
還是考慮在定義變量的作用域下規避掉因為java實現問題導致上面的結果返回...我既然沒有nonlocal
這樣的機制(畢竟不支持引用傳遞....),索性就用final
限制起來.
嗚呼哀哉
參考資料: