• 规避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

  • 相关阅读:
    vue_组件化开发
    C++ / C# 访问网络共享文件夹
    PetaLinux 设置操作系统内存
    linux 不用./ 直接执行程序
    Visual Studio Code 开发环境搭建 —— C# 扩展插件
    Visual Studio Code 调试项目时传参
    PetaLinux 安装
    Ubuntu 报 "xxx is not in the sudoers file.This incident will be reported" 错误解决方法
    常用 Linux 命令
    搭建 Git 服务器(Ubuntu 系统)
  • 原文地址:https://www.cnblogs.com/lingyejun/p/15833520.html
Copyright © 2020-2023  润新知