Java中如何使用接口或同类机制消除CSV输出的重复代码?
消除重复写入逻辑的实现方案
核心思路
所有写入方法的公共逻辑完全一致,仅存在两个可变差异点:
- 从原始传入对象中提取对应子对象列表的获取逻辑
- 将子对象列表转换为CSV行数据的转换逻辑
可以通过函数式接口将两个差异点作为参数传入通用方法,把所有公共的日志、文件创建、写CSV、异常捕获逻辑统一收口,彻底消除重复代码。
上述实现依赖Java 8+内置的函数式接口
Function,无需额外引入第三方依赖,同时可以在编译期校验方法引用的正确性,解决原代码中使用Object作为参数带来的类型不安全问题。
具体实现
1. 抽通用核心方法
在Writer类中定义带泛型的通用写入方法,覆盖所有公共流程:
// T: 传入的顶层对象类型,R: 子对象类型 private <T, R> void writeGeneric(T obj, File dir, String filename, Boolean append, Function<T, List<R>> subObjectExtractor, Function<List<R>, List<String[]>> dataConverter) { try { log.info("writing " + filename + "..."); // 差异点1:提取对应子对象列表 List<R> subObjectList = subObjectExtractor.apply(obj); // 差异点2:转换为CSV可写入的字符串数组格式 List<String[]> csvData = dataConverter.apply(subObjectList); // 公共流程:创建文件、写入CSV final File out = CreateFileObject(dir, filename); writeCsv(csvData, out, append); } catch (Exception e) { // 优化点:日志直接带文件名,排查问题更方便 log.info("Error in write: " + filename); log.error(e); e.printStackTrace(); } }
2. 原有写入方法简化
原来重复实现的两个writeSubOject1、writeSubOject2方法,只需要把两个差异点作为方法引用传入通用方法即可,不需要重复写所有公共逻辑:
// 注意:将原代码里的Object替换为实际的顶层类YourTopLevelObj,保证类型安全 public void writeSubOject1(YourTopLevelObj obj, File dir, String filename, Boolean append) { writeGeneric(obj, dir, filename, append, YourTopLevelObj::getSubObject1, // 传入子对象获取逻辑 SubObject1::getData // 传入数据转换逻辑 ); } public void writeSubOject2(YourTopLevelObj obj, File dir, String filename, Boolean append) { writeGeneric(obj, dir, filename, append, YourTopLevelObj::getSubObject2, SubObject2::getData ); }
进阶优化(配置驱动批量导出)
如果你的业务场景是每次循环都要导出所有子对象,甚至可以进一步简化调用逻辑,把每个导出项的规则做成配置,后续新增导出项只需要加一行配置,完全不用修改核心写入代码:
- 定义导出配置类(Java 16+可用record简化,低版本用普通POJO即可):
public record ExportConfig<T, R>( String filename, Function<T, List<R>> extractor, Function<List<R>, List<String[]>> dataConverter ) {}
- 在Writer类中维护所有导出规则,提供批量导出方法:
// 预定义所有导出规则,新增导出项只需在此处追加一行 private static final List<ExportConfig<YourTopLevelObj, ?>> EXPORT_RULES = List.of( new ExportConfig<>("subObject1.csv", YourTopLevelObj::getSubObject1, SubObject1::getData), new ExportConfig<>("subObject2.csv", YourTopLevelObj::getSubObject2, SubObject2::getData) ); // 批量导出方法 public void exportAll(YourTopLevelObj obj, File dir, Boolean append) { EXPORT_RULES.forEach(rule -> writeGeneric( obj, dir, rule.filename(), append, (Function<YourTopLevelObj, List<?>>) rule.extractor(), (Function<List<?>, List<String[]>>) rule.dataConverter() )); }
上述代码中的强转是安全的:所有导出规则在初始化时已经绑定了明确的子对象类型,不会出现运行时类型转换异常。
- 原来的循环调用逻辑可以简化为单步:
for (YourTopLevelObj obj : objects) { writer.exportAll(obj, dir, true); }
内容的提问来源于stack exchange,提问作者thePandasFriend
相关产品推荐
相关产品推荐

