Lambda表达式中的"变量"
1.场景复现
执行分页查询时,需要对查出来的数据封装页码,定义一个Integer用于递增表示页码,这里采用lambda表达式的规范写法,在表达式中封装并递增页码,但是出现报错
Integer count=1;
final IPage<RespDTO> convert = page.convert(x -> {
final RespDTO respDTO =new RespDTO();
BeanUtil.copyProperties(x,respDTO);
respDTO.setNumber(count);
count++;
return SalesPointRespDTO;
});
报错:Variable used in lambda expression should be final or effectively final
2.原理
由报错信息就可以得知,Lambda表达式中的变量必须是实质上final的(effectively final),这样设计的原因:
- 线程安全:Lambda表达式可能会被用于多线程环境,例如作为并发任务的一部分。如果Lambda表达式内部的变量可以被外部修改,那么这些变量就不是线程安全的,可能会导致不可预测的行为和错误。通过限制Lambda表达式只能使用final变量,Java确保了Lambda表达式的行为是确定的。
- 不变性:Lambda表达式的不变性(immutability)使得它们可以被安全地传递给不同的线程,或者存储在数据结构中,而不需要担心它们的状态会被改变。这种不变性是函数式编程的一个关键特性。
- 避免副作用:在函数式编程中,一个重要的概念是避免副作用(side effects),即函数的执行不应该改变外部状态。通过限制Lambda表达式只能使用final变量,Java鼓励开发者编写无副作用的代码,这有助于提高代码的可读性和可维护性。
3.解决方案
这里采用一个类似于欺骗编译器的方法:即定义一个final数组,因为即使数组本身被视为final的,数组的不同元素也可以被修改,我们可以利用数组的第一项作为count
final Integer[] number = {(parseReqParam.getPageNo() - 1) * parseReqParam.getPageSize() + 1};//计算第一项序号
final IPage<RespDTO> convert = page.convert(x -> {
final RespDTO respDTO =new RespDTO();
BeanUtil.copyProperties(x,respDTO);
respDTO.setNumber(number[0]);
number[0]++;
return SalesPointRespDTO;
});