规避Variable used in lambda expression should be final or effectively final而引发了方法参数值拷贝的问题
背景
今天组里面有一个新同事小A向我求助了一个问题,比较典型也是新人很容易犯的问题,特此记录下来。
他写了一个类似于下面的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 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); } } |
问题
一开始是编译不过去的
1 | 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;对象赋值的方法拷贝出来放到了方法里面
1 2 3 | 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中,所以将方法改成如下就可以了
1 2 3 | private static void copyMap(Map<String, List<Phone>> sourceMap, Map<String, List<Phone>> targetMap) { targetMap.putAll(sourceMap); } |
本篇文章如有帮助到您,请给「翎野君」点个赞,感谢您的支持。
出处:http://www.cnblogs.com/lingyejun/
若本文如对您有帮助,不妨点击一下右下角的【推荐】。
如果您喜欢或希望看到更多我的文章,可扫描二维码关注我的微信公众号《翎野君》。
转载文章请务必保留出处和署名,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!