Lambda表達式中引用變量的問題
Lambda表達式內部自定義的變量肯定沒問題。引用的外部final變量也沒問題。問題在於effectively final變量的理解,及應用場景的認識。引用的外部變量沒有加final限定符,只要沒有發生過改變,就可以當作是事實上的final變量。變量沒改變過,就是說Lambda表達式引用的外部變量在它所在的作用域范圍內,只賦值過一次**,該變量名稱只出現過一次**,就算是effectively final。舉例說明。
例1:增強for循環,實質上使用的是迭代器(參見例3)。
String[] array = {"a", "b", "c"}; for(Integer i : Arrays.asList(1,2,3)){ Stream.of(array).map(item -> Strings.padEnd(item, i, '@')) .forEach(System.out::println); }
在例1中,雖然for循環代碼塊中變量 i 的值是變化的,但是 i 只出現過一次,沒有明顯的改變,也算事實上的final,Lambda表達式能通過。與之相反的示例,看例2。
例2:普通for循環。
String[] array = {"a", "b", "c"}; for(Integer i = 1; i<4; i++){ Stream.of(array).map(item -> Strings.padEnd(item, i, '@')) .forEach(System.out::println); }
在例2中,Lambda表達式引用的外部變量 i 所在的作用域范圍是for循環體。明顯有兩次賦值 i =1 , i++。要報錯誤提示:Variable used in lambda expression should be final or effectively final。變量 i 不能算是effectively final。
例3:迭代器。
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); }
增加for循環也會轉為迭代器調用模式。可以明顯看出變量 i 只出現過一次有效賦值,沒有修改過,算是effectively final。Lambda表達式能通過。
例4: 將普通for循環變量賦給一個中間變量,可通過。
String[] array = {"a", "b", "c"}; for(Integer i = 1; i<4; i++){ int k = i; Stream.of(array).map(item -> Strings.padEnd(item, k, '@')) .forEach(System.out::println); }
因為變量 k 的作用域是for循環的一次{},變量 k 在一次{}代碼塊中只賦值過一次后,沒有再被改變過,即effectively final。Lambda表達式能通過。再舉兩個很近似的例子,就更能說明問題。
例4-2: 變量的判斷不算是被修改。
String[] array = {"a", "b", "c"}; for(Integer i = 1; i<4; i++){ int k = i; if(k>2){ System.out.println(k); } Stream.of(array).map(item -> Strings.padEnd(item, k, '@')) .forEach(System.out::println); }
該Lambda表達式能通過。因為 k 只有過一次賦值修改后就沒有改變過。k 的后兩次出現只是讀取使用,不是寫入。
例4-3: 自己賦給自己也算是再次被修改。
String[] array = {"a", "b", "c"}; for(Integer i = 1; i<4; i++){ int k = i; k = k; Stream.of(array).map(item -> Strings.padEnd(item, k, '@')) .forEach(System.out::println); }
這個Lambda表達式也會報錯。因為變量 k 出現了兩次賦值,不符合final的定義。這樣多舉幾個例子測試,就算徹底搞明白了effectively final的含義及應用場景了。
Lambda表達式中引用變量的問題
Lambda表達式內部自定義的變量肯定沒問題。引用的外部final變量也沒問題。問題在於effectively final變量的理解,及應用場景的認識。引用的外部變量沒有加final限定符,只要沒有發生過改變,就可以當作是事實上的final變量。變量沒改變過,就是說Lambda表達式引用的外部變量在它所在的作用域范圍內,只賦值過一次**,該變量名稱只出現過一次**,就算是effectively final。舉例說明。
例1:增強for循環,實質上使用的是迭代器(參見例3)。
String[] array = {"a", "b", "c"};
for(Integer i : Arrays.asList(1,2,3)){
Stream.of(array).map(item -> Strings.padEnd(item, i, '@'))
.forEach(System.out::println);
}
在例1中,雖然for循環代碼塊中變量 i 的值是變化的,但是 i 只出現過一次,沒有明顯的改變,也算事實上的final,Lambda表達式能通過。與之相反的示例,看例2。
例2:普通for循環。
String[] array = {"a", "b", "c"};
for(Integer i = 1; i<4; i++){
Stream.of(array).map(item -> Strings.padEnd(item, i, '@'))
.forEach(System.out::println);
}
在例2中,Lambda表達式引用的外部變量 i 所在的作用域范圍是for循環體。明顯有兩次賦值 i =1 , i++。要報錯誤提示:Variable used in lambda expression should be final or effectively final。變量 i 不能算是effectively final。
例3:迭代器。
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);
}
增加for循環也會轉為迭代器調用模式。可以明顯看出變量 i 只出現過一次有效賦值,沒有修改過,算是effectively final。Lambda表達式能通過。
例4: 將普通for循環變量賦給一個中間變量,可通過。
String[] array = {"a", "b", "c"};
for(Integer i = 1; i<4; i++){
int k = i;
Stream.of(array).map(item -> Strings.padEnd(item, k, '@'))
.forEach(System.out::println);
}
因為變量 k 的作用域是for循環的一次{},變量 k 在一次{}代碼塊中只賦值過一次后,沒有再被改變過,即effectively final。Lambda表達式能通過。再舉兩個很近似的例子,就更能說明問題。
例4-2: 變量的判斷不算是被修改。
String[] array = {"a", "b", "c"};
for(Integer i = 1; i<4; i++){
int k = i;
if(k>2){
System.out.println(k);
}
Stream.of(array).map(item -> Strings.padEnd(item, k, '@'))
.forEach(System.out::println);
}
該Lambda表達式能通過。因為 k 只有過一次賦值修改后就沒有改變過。k 的后兩次出現只是讀取使用,不是寫入。
例4-3: 自己賦給自己也算是再次被修改。
String[] array = {"a", "b", "c"};
for(Integer i = 1; i<4; i++){
int k = i;
k = k;
Stream.of(array).map(item -> Strings.padEnd(item, k, '@'))
.forEach(System.out::println);
}
這個Lambda表達式也會報錯。因為變量 k 出現了兩次賦值,不符合final的定義。這樣多舉幾個例子測試,就算徹底搞明白了effectively final的含義及應用場景了。