lambda運算式訪問外部變數有一個非常重要的限制:變數不可變。
請問為什么:
String[] array = {"a", "b", "c"};
for(Integer i : Lists.newArrayList(1,2,3)){
Stream.of(array).map(item -> Strings.padEnd(item, i, '@')).forEach(System.out::println);
}可以執行呢?
而:
String[] array = {"a", "b", "c"};
for(int i = 1; i<4; i++){
Stream.of(array).map(item -> Strings.padEnd(item, i, '@')).forEach(System.out::println);
}就不可以?這兩個i都是區域變數,并且不是final呀?
難道說在lambda陳述句執行程序中不可變就可以了嗎?我覺得不太對,我查了一些for each的源代碼,是不是因為第一個里的i指向的是迭代器里的next方法回傳的物件這是不變的,而根據
public E next() {
checkForComodification();
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
next方法回傳的物件是改變的。所以臨時變數i是final的。
不知道理解的對不對,希望各位大神可以指點一下,謝謝
uj5u.com熱心網友回復:
上面的第一種增強for回圈,可以改寫成迭代陳述句,這樣看得清楚一些:String[] array = {"a", "b", "c"};
List<Integer> list = Arrays.asList(1, 2, 3);
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext() ){
int i = iterator.next();
Stream.of(array).map(item -> Strings.padEnd(item, i, '@'))
.forEach(System.out::println);
}
在while開始的陳述句塊中,i只出現一次,符合efficient final的條件。
而普通for回圈中的lambda運算式中參考的外部變數i,可以看出有明顯的改變痕跡。因而是不允許的。
uj5u.com熱心網友回復:
主要看lambda運算式參考的外部變數的作用域的范圍。如果在其作用范圍內變數沒有修改過(變數沒有出現過兩次),哪怕沒有final限制符,也是算作effectively final的。uj5u.com熱心網友回復:
for(int i=0;i<100;i++) {int k = i;
k =k;
pool.execute(() -> {
for (int j = 0; j < 1000; j++) {
System.out.println(k);
mydata2.addPlusPlus();
}
System.out.println(Thread.currentThread().getName()+" -- "+mydata2.number);
});
}
從上面一段代碼可以很清楚得看出,現在變數k是不能用的。但是,去掉k = k這一行,那么變數k在{}這一段里的作用域范圍內就可以看作沒變過,算作effectively final,能通過lambda運算式。
uj5u.com熱心網友回復:
是因為,最終lambda運算式會被編譯器編譯成一個內部類。而內部類訪問區域變數,必須要求該區域變數不可變(即樓上所說的effectively final,只有一次賦值操作)。為什么?因為Java不支持閉包,一個類(包括內部類)可以訪問另一個類的成員變數,但是沒法訪問另一個類方法內的區域變數。那Java對內部類是如何支持訪問區域變數的呢?答案是,編譯器會將被參考到的區域變數,隱式地傳遞給該內部類,作為它的成員變數,正是因為這種做法,所以必須要求,區域變數應該要是不可變的。具體可以參考下這個回答
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/111712.html
標籤:Java相關
上一篇:sort對陣列排序問題求助
