天天看點

簡單說明:list.forEach 中變量必須為 final 的問題

可能有些人沒遇到過 list.forEach 中變量必須為 final 的問題,那就先舉兩個例子

示例1,如下:

public static void main(String[] args) {
    List<String> stringList = new ArrayList<>();
    stringList.add("a");
    stringList.add("b");
    stringList.add("c");
    stringList.add("d");
    int i = 0;
    stringList.forEach(s -> {
        System.out.println(s + i);
    });
}           

示例2,如下:

public static void main(String[] args) {
    List<String> stringList = new ArrayList<>();
    stringList.add("a");
    stringList.add("b");
    stringList.add("c");
    stringList.add("d");
    int i = 0;
    stringList.forEach(s -> {
        System.out.println(s + i);
        // 示例1與示例2的差別
        i = i + 1
    });
}           

咋一看,兩個示例沒什麼問題,都能正常運作。但是實際運作結果如下。

示例1運作結果

a0
b0
c0
d0           

而示例2無法運作,編輯器給了如下提示

Error:(16, 36) java: 從lambda 表達式引用的本地變量必須是最終變量或實際上的最終變量           

要把示例2修正為可以運作的代碼,可以做如下修正:

public static void main(String[] args) {
    List<String> stringList = new ArrayList<>();
    stringList.add("a");
    stringList.add("b");
    stringList.add("c");
    stringList.add("d");
    final int[] i = {0};
    stringList.forEach(s -> {
        System.out.println(s + i[0]);
        i[0] = i[0] + 1;
    });
}           

就以上的現象,用三個問題來簡單說明。

問題一,為什麼示例2的 int i = 0 必須用 final 修飾?

答:forEach 在此處使用的是 lambda 表達式,可以簡單的把 lambda 表達式 了解為匿名内部類(lambda 表達式不僅僅是内部類這麼簡單)。而匿名内部類的變量必須用 final 修飾。

問題二,為什麼匿名内部類的變量必須用 final 修飾?

答:類的生命周期比方法的生命周期長,同理匿名類的生命周期比方法的生命周期長。換句話說,方法運作完了,變量釋放了,但是匿名内部類還在。這時就要求匿名内部類引用的變量必須還在,這樣才能保持資料的一緻性。

問題三,為什麼變量 int i 要改為數組 int[] i?

答:因為 final int i 中,i 的值是無法改變的,但是方法中需要一個可以改變的變量。在 final int[] i 中,i 的引用位址是不變的,但是 i 的屬性是可以改變的。

以上隻是簡單的說明,便于大家了解。大家可以繼續深究一下裡面的知識點。

如果文章有幫助到了你,歡迎點贊、轉發。

如果文章有錯誤的地方,歡迎留言交流。