Avoiding Variable used in lambda expression should be final or effectively final causes the problem of copying method parameter values

background

Today, A new colleague in the group, Xiao A, asked me for help. It is A typical problem that newcomers are easy to make. I hereby record it.

He wrote a code similar to the following

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", "silver", "64GB", 8699);
        Phone phone2 = new Phone(2, "iPhone 11 Pro", "silver", "64GB", 8700);
        Phone phone3 = new Phone(3, "iPhone 11 Pro Max", "silver", "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));

            // In this way, the otherMap used in the forEach loop below cannot be compiled,
            // Variable used in lambda expression should be final or effectively final
            //otherMap = phoneMap;

            // This logic can be bypassed by putting logic into a method
            copyMap(otherMap, phoneMap);

        }

        queryPhoneNameList.forEach(queryPhoneName -> {
            otherMap.get(queryPhoneName);
        });

    }

    private static void copyMap(Map<String, List<Phone>> sourceMap, Map<String, List<Phone>> targetMap) {
        // Method parameters are passed by value, so this assignment will not take effect
        targetMap = sourceMap;
        // Just change it to the following way
        targetMap.putAll(sourceMap);
    }
}

problem

It was impossible to compile at first

Variable used in lambda expression should be final or effectively final

In other words, only the outer local variable marked final can be referenced in the lambda expression, or although it is not explicitly defined as final, it is actually a final variable, otherwise there will be a compilation error.

Obviously, the otherMap variable in the above code is in map < string, list < phone > > otherMap = new HashMap < > (); After initialization, another assignment operation is performed. otherMap = phoneMap; It has been modified twice, so the compiler thinks this is not a final variable, so it reports an error.

However, we can use some skills to avoid this error report, such as the writing method of Xiao A, who puts otherMap = phoneMap; The method of object assignment is copied and put into the method

    private static void copyMap(Map<String, List<Phone>> sourceMap, Map<String, List<Phone>> targetMap) {
        targetMap = sourceMap;
    }

Then the problem arises. Xiao a debugs and finds that the copyMap(phoneMap, otherMap) has been executed; After that, otherMap is still empty. Then, ryuno suddenly saw the way, and then told him that method parameter passing in java is actually value passing. He also wrote an article before Discriminating value passing and reference passing in Java method parameters.

After reading this article, I believe you will understand the original, because Map has putAll() It copies all the elements of one Map to another, so change the method to the following

    private static void copyMap(Map<String, List<Phone>> sourceMap, Map<String, List<Phone>> targetMap) {
        targetMap.putAll(sourceMap);
    }

If this article is helpful to you, please give "Lingye Jun" a praise. Thank you for your support.

Launch link: Avoiding Variable used in lambda expression should be final or effectively final causes the problem of copying method parameter values - Lingye Jun - blog Garden

Keywords: Java Back-end

Added by MattSharp on Sat, 22 Jan 2022 18:51:16 +0200