規避Variable used in lambda expression should be final or effectively final而引發了方法參數值拷貝的問題


背景

今天組里面有一個新同事小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);
    }

  

本篇文章如有幫助到您,請給「翎野君」點個贊,感謝您的支持。

首發鏈接:https://www.cnblogs.com/lingyejun/p/15833520.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM