編譯器說 Lambda 表示式中的變數必須是 final 的,我偏不信
偶爾,我們需要在 Lambda 表示式中修改變數的值,但如果直接嘗試修改的話,編譯器不會視而不見聽而不聞,它會警告我們說:“variable used in lambda expression should be final or effectively final”。
這個問題發生的原因是因為 Java 規範中是這樣規定的:
Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression
must either be declared final or be effectively final (§4.12.4),
or a compile-time error occurs where the use is attempted.
大致的意思就是說,Lambda 表示式中要用到的,但又未在 Lambda 表示式中宣告的變數,必須宣告為 final 或者是 effectively final,否則就會出現編譯錯誤。
關於 final 和 effectively final 的區別,可能有些小夥伴不太清楚,這裡多說兩句。
final int a;
a = 1;
// a = 2;
// 由於 a 是 final 的,所以不能被重新賦值
int b;
b = 1;
// b 此後再未更改
// b 就是 effectively final
int c;
c = 1;
// c 先被賦值為 1,隨後又被重新賦值為 2
c = 2;
// c 就不是 effectively final
明白了 final 和 effectively final 的區別後,我們瞭解到,如果把 limit 定義為 final,那就無法在 Lambda 表示式中修改變數的值。那有什麼好的解決辦法呢?既能讓編譯器不發出警告,又能修改變數的值。
思前想後,試來試去,我終於找到了 3 個可行的解決方案:
1)把 limit 變數宣告為 static。
2)把 limit 變數宣告為 AtomicInteger。
3)使用陣列。
下面我們來詳細地一一介紹下。
01、把 limit 變數宣告為 static
要想把 limit 變數宣告為 static,就必須將 limit 變數放在 main()
方法外部,因為 main()
方法本身是 static 的。完整的程式碼示例如下所示。
public class ModifyVariable2StaticInsideLambda {
static int limit = 10;
public static void main(String[] args) {
Runnable r = () -> {
limit = 5;
for (int i = 0; i < limit; i++) {
System.out.println(i);
}
};
new Thread(r).start();
}
}
來看一下程式輸出的結果:
0
1
2
3
4
OK,該方案是可行的。
02、把 limit 變數宣告為 AtomicInteger
AtomicInteger 可以確保 int 值的修改是原子性的,可以使用 set()
方法設定一個新的 int 值,get()
方法獲取當前的 int 值。
public class ModifyVariable2AtomicInsideLambda {
public static void main(String[] args) {
final AtomicInteger limit = new AtomicInteger(10);
Runnable r = () -> {
limit.set(5);
for (int i = 0; i < limit.get(); i++) {
System.out.println(i);
}
};
new Thread(r).start();
}
}
來看一下程式輸出的結果:
0
1
2
3
4
OK,該方案也是可行的。
03、使用陣列
使用陣列的方式略帶一些欺騙的性質,在宣告陣列的時候設定為 final,但更改 int 的值時卻修改的是陣列的一個元素。
public class ModifyVariable2ArrayInsideLambda {
public static void main(String[] args) {
final int [] limits = {10};
Runnable r = () -> {
limits[0] = 5;
for (int i = 0; i < limits[0]; i++) {
System.out.println(i);
}
};
new Thread(r).start();
}
}
來看一下程式輸出的結果:
0
1
2
3
4
OK,該方案也是可行的。
04、鳴謝
好了,親愛的讀者朋友,以上就是本文的全部內容了,是不是感覺挺有意思的,編譯器告訴我們要用 final 修飾 Lambda 表示式外的變數,但我們卻找到了其他的解決方案,還一找就是 3 個,是不是感覺技能包又升級了,有沒有?伸出小手給自己點個贊?吧。
PS:本篇文章中的示例程式碼已經同步到碼雲,傳送門~
相關文章
- Java 中將lambda 表示式體中的變數賦值給lambda體之外的一個區域性變數時,要求那個區域性變數是final 修飾的Java變數賦值
- C++ 接受狀態變數的lambda表示式C++變數
- Java 中的 Lambda 表示式Java
- 必知必會之Lambda表示式
- Python中lambda表示式的用法Python
- Java中Lambda表示式的使用Java
- Java中Lambda表示式的應用Java
- Java8中的Lambda表示式Java
- cpp的lambda表示式
- Java的Lambda表示式Java
- Java中Lambda表示式的進化之路Java
- C# Lambda表示式詳解,及Lambda表示式樹的建立C#
- 提高生產力!這10個Lambda表示式必須掌握,開發效率嘎嘎上升!
- lambda 表示式使用的方式
- Lambda 表示式的應用
- Java中的函數語言程式設計(三)lambda表示式Java函數程式設計
- JDK1.8中Lambda表示式的應用JDK
- lambda 表示式
- lambda表示式
- [譯]JavaScript的新功能將改變正規表示式的編寫方式JavaScript
- Java中lambda表示式詳解Java
- lambda表示式的寫法1
- Java8的Lambda表示式Java
- 利用 Lambda 表示式實現 Java 中的惰性求值Java
- Python中lambda表示式的語法與應用Python
- 利用Lambda表示式進行Java中的惰性求值Java
- 八,Lambda表示式
- Python Lambda 表示式Python
- Java Lambda表示式Java
- 【Kotlin】Lambda表示式Kotlin
- kotlin lambda表示式Kotlin
- CPP lambda表示式
- Python - lambda 表示式Python
- Java | Lambda表示式Java
- Lambda表示式(Java)Java
- 說說 方舟編譯器編譯
- 說說 Spring 表示式語言(SpEL)中的各種表示式型別Spring型別
- Java8特性詳解 lambda表示式(二):流式處理中的lambdaJava