背景
函数式方法
/**
* 生成Excel文件并在使用后自动清理
* @param data 数据列表
* @param clazz 数据类型Class
* @param fileName 文件名
* @param sheetName sheet名称
* @param sheetNo sheet编号
* @param function 文件处理函数
*/
public static <T, R> R generateAndProcessExcelFile(List<T> data,
Class<T> clazz,
String fileName,
String sheetName,
Integer sheetNo,
Function<Path, R> function) {
Path path = null;
try {
path = generateExcelFile(data, clazz, fileName, sheetName, sheetNo, null);
return function.apply(path);
} finally {
// 自动清理临时文件
if (path != null) {
try {
Files.deleteIfExists(path);
} catch (Exception e) {
log.warn("自动删除临时文件失败: {}", path, e);
}
}
}
}
调用该工具类方法代码
public static <T> String writeMsg2Excel(String fileName,
List<T> dtoList,
Class<T> clazz,
int sheetNo,
String sheetName,
long expireTime,
TimeUnit unit) {
long expireSecondLong = unit.toSeconds(expireTime);
Integer expireSecond = (int) expireSecondLong;
FileClient fileClient = SpringUtil.getNonnullBean(FileClient.class);
FileUploadDTO fileUploadDTO =
// fileName被lambada方法调用,不能在外面添加后缀
FileUtil.generateAndProcessExcelFile(dtoList, clazz, fileName + ExcelTypeEnum.XLSX.getValue(), sheetName, sheetNo,
path -> {
// 这里对文件过期时间没有专门设置,默认2099,方法属于通用方法,不敢加过期时间
FileUploadParam fileUploadParam =
FileUploadParam.simpleBuild(path.toFile(), expireSecond, com.test.file.common.FileLevelEnum.S3);
fileUploadParam.setFileName(fileName + ExcelTypeEnum.XLSX.getValue());
fileUploadParam.getDownUrlParam().setDownloadName(fileName + ExcelTypeEnum.XLSX.getValue());
return fileClient.uploadFile(fileUploadParam);
});
return fileUploadDTO.getFileUrl();
}
为什么expireSecond没有作为入参给到函数式接口,却可以在lambda中正常使用?
Lambda 表达式可以访问其外部作用域中的:
- 局部变量(必须是 effectively final)
- 实例变量
- 静态变量
expireSecond 属于第一种情况,虽然它并没有标记final
- expireSecond 是在 Lambda 表达式外部定义的局部变量
- 它在 Lambda 表达式内部被引用时,Java 会自动捕获这个变量
- 由于 expireSecond 在之后没有被重新赋值,它被视为"实际上的最终变量"(effectively final)
如果expireSecond在方法中有赋值操作,整个方法会报错。
编译器是如何处理的
编译器会在内部将 expireSecond 包装成 Lambda 表达式的上下文
expireSecond也可以作为入参传入
/**
* 生成并处理Excel文件
* <p>
* 该方法首先根据提供的数据生成Excel文件,然后使用指定的函数对接生成的文件进行处理,
* 处理完成后会自动清理生成的临时文件。
*
* @param <T> 数据类型
* @param <R> 返回值类型
* @param data 要写入Excel的数据列表
* @param clazz 数据对象的Class类型,用于反射获取字段信息
* @param fileName Excel文件名
* @param sheetName Excel工作表名称
* @param expireSecond 过期时间(秒),用于文件处理时的时间限制
* @param function 处理Excel文件的函数,接收文件路径和过期时间作为参数
* @return 处理结果
*/
public static <T, R> R generateAndProcessExcelFile(List<T> data,
Class<T> clazz,
String fileName,
String sheetName,
Integer expireSecond,
BiFunction<Path, Integer, R> function) {
Path path = null;
try {
path = generateExcelFile(data, clazz, fileName, sheetName, null);
return function.apply(path, expireSecond);
} finally {
// 自动清理临时文件
if (path != null) {
try {
Files.deleteIfExists(path);
} catch (Exception e) {
log.warn("自动删除临时文件失败: {}", path, e);
}
}
}
}