Lambda表达式变量需为final/有效final:同一对象为何两处表现不同?
问题描述
我定义了如下final常量:
// Constants.java public static final List<Integer> SPECIAL_CASE = Arrays.asList(2, 3, 6, 7);
这是一个Integer类型的数值列表。我的服务层代码如下:
// ReportServiceImpl.java public class ReportServiceImpl { /////////// Report repObj = new Report(); BeanUtils.copyProperties(repPending, repObj); .......... ////// if(Constants.SPECIAL_CASE.stream().anyMatch(element -> repObj.getSpecialCase() == element)) { System.out.println("It is a Special report..."); } if((Constants.SPECIAL_CASE.stream().anyMatch(element -> repObj.getSpecialCase() == element)) || ((repObj.getMp() == null || repObj.getMp() == 0) && (repObj.getMPro() == null || repObj.getMPro() == 0)) ){ // do something ..... ...// } ///////// }
repObj通过BeanUtils从另一个对象复制属性得到,我在if条件中使用该对象。第一个if判断repObj的specialCase是否匹配列表值,运行正常;但第二个if中使用相同的repObj的Lambda表达式时,出现编译错误**‘Variable used in lambda expression should be final or effectively final’**。请问为何会出现这种差异,该如何解决?
原因分析
第一个if能正常编译,第二个不行的核心原因是编译器对复合条件语句的变量有效性检查更严格:
- 第一个if是单一的Lambda判断逻辑,编译器能确定
repObj在整个判断过程中没有被修改,属于effectively final(实际终态,即从未被重新赋值),符合Lambda的变量捕获要求; - 第二个if是
Lambda条件 || 其他条件的复合逻辑,编译器会扫描整个if语句的上下文,哪怕当前代码没有显式修改repObj,它也会假设||分支中存在修改变量的潜在可能,因此不认定repObj为effectively final,进而触发编译错误。
解决方法
有两种简单可行的方案:
- 提前提取判断结果复用
把Lambda的判断逻辑提前存入一个变量,后续直接复用这个变量,既解决编译问题,还避免重复流式计算:
// 提取判断结果,变量自动为effectively final(也可显式加final) boolean isSpecialCase = Constants.SPECIAL_CASE.stream().anyMatch(element -> repObj.getSpecialCase() == element); // 第一个if使用提取的变量 if(isSpecialCase) { System.out.println("It is a Special report..."); } // 第二个if直接复用变量 if(isSpecialCase || ((repObj.getMp() == null || repObj.getMp() == 0) && (repObj.getMPro() == null || repObj.getMPro() == 0)) ){ // do something }
- 将
repObj声明为final
直接在声明repObj时加上final关键字,明确告诉编译器该变量不会被重新赋值:
final Report repObj = new Report(); BeanUtils.copyProperties(repPending, repObj);
这样无论在哪个if的Lambda中使用,编译器都会认定它是final变量,符合Lambda的捕获规则。
内容的提问来源于stack exchange,提问作者Geek
相关产品推荐
相关产品推荐

