背景
今天組里面有一個新同事小A向我求助了一個問題,比較典型也是新人很容易犯的問題,特此記錄下來。
他寫了一個類似於下面的代碼
package com.lingyejun.dating.chap11.toutiao;
import java.util.*;
import java.util.stream.Collectors;
public class StreamMapCopy {
public static List<Phone> initPhoneList() {
List<Phone> phones = new ArrayList<>();
Phone phone1 = new Phone(1, "iPhone 11 Pro", "銀色", "64GB", 8699);
Phone phone2 = new Phone(2, "iPhone 11 Pro", "銀色", "64GB", 8700);
Phone phone3 = new Phone(3, "iPhone 11 Pro Max", "銀色", "64GB", 8900);
phones.add(phone1);
phones.add(phone2);
phones.add(phone3);
return phones;
}
public static void main(String[] args) {
List<String> queryPhoneNameList = Arrays.asList("iPhone 11 Pro", "HuaWei", "Oppo", "Vivo");
Map<String, List<Phone>> otherMap = new HashMap<>();
if (queryPhoneNameList.size() > 0) {
Map<String, List<Phone>> phoneMap = initPhoneList().stream()
.filter(a -> queryPhoneNameList.contains(a.getProductName()))
.collect(Collectors.groupingBy(Phone::getProductName));
// 這種寫法下面的forEach循環中使用到的otherMap編譯不過去,
// Variable used in lambda expression should be final or effectively final
//otherMap = phoneMap;
// 將邏輯放到方法中可以繞過此邏輯
copyMap(otherMap, phoneMap);
}
queryPhoneNameList.forEach(queryPhoneName -> {
otherMap.get(queryPhoneName);
});
}
private static void copyMap(Map<String, List<Phone>> sourceMap, Map<String, List<Phone>> targetMap) {
// 方法參數是值傳遞,故這種賦值是不會生效的
targetMap = sourceMap;
// 改為下面的方式就可以了
targetMap.putAll(sourceMap);
}
}
問題
一開始是編譯不過去的
Variable used in lambda expression should be final or effectively final
翻譯過來就是說在lambda表達式中只能引用標記了 final 的外層局部變量或者雖然沒有顯式定義為final,但實際上就是一個final變量,否則會編譯錯誤。
那么顯然在上面的代碼中的otherMap變量,在Map<String, List<Phone>> otherMap = new HashMap<>();初始化以后,又進行了一次賦值操作otherMap = phoneMap;進行了二次修改,所以編譯器認為這不是一個final變量故而報錯。
但是我們可以用一些技巧來規避掉這個報錯,比如小A的寫法,他將otherMap = phoneMap;對象賦值的方法拷貝出來放到了方法里面
private static void copyMap(Map<String, List<Phone>> sourceMap, Map<String, List<Phone>> targetMap) {
targetMap = sourceMap;
}
然后問題就出現了,小A debug了一下發現執行完copyMap(phoneMap, otherMap);之后otherMap仍然是空的,然后翎野君一下子看出了其中的門道,然后給他講了一下java中方法參數傳遞實際上是值傳遞的,之前還專門寫過一篇文章辨析Java方法參數中的值傳遞和引用傳遞。
將這個文章看完相信大家就懂得了其中的原有,因為Map有putAll().它把一個Map的所有元素全部復制到另一個Map中,所以將方法改成如下就可以了
private static void copyMap(Map<String, List<Phone>> sourceMap, Map<String, List<Phone>> targetMap) {
targetMap.putAll(sourceMap);
}
本篇文章如有幫助到您,請給「翎野君」點個贊,感謝您的支持。
