Java中如何将方法名作为参数传递以消除重复代码
是否存在方法名参数化的实现方案?
基础代码示例
public class JournalLine { BigDecimal ccyAmount; BigDecimal lcyAmount; BigDecimal rptAmount; // 省略Getters和Setters }
原有冗余实现
原有三段求和逻辑结构完全一致,仅调用的getter方法不同,存在明显代码重复,且这类逻辑会在应用不同位置被调用,不一定同时出现:
// Calculate totals BigDecimal totalCcyAmount = journalLines.stream() .map(journalLine -> journalLine.getCcyAmount()) .reduce((a, b) -> a.add(b)) .orElse(BigDecimal.ZERO); BigDecimal totalLclAmount = journalLines.stream() .map(journalLine -> journalLine.getLclAmount()) .reduce((a, b) -> a.add(b)) .orElse(BigDecimal.ZERO); BigDecimal totalRptAmount = journalLines.stream() .map(journalLine -> journalLine.getRptAmount()) .reduce((a, b) -> a.add(b)) .orElse(BigDecimal.ZERO);
期望实现目标
希望通过传入方法标识的方式抽取通用求和逻辑,减少重复代码,期望调用形式如下:
//Calculate totals and pass the method name BigDecimal totalCcyAmount = getTotal(journalLines, "getCcyAmount"); BigDecimal totalLclAmount = getTotal(journalLines, "getLclAmount"); BigDecimal totalRptAmount = getTotal(journalLines, "getRptAmount"); public BigDecimal getTotal( List<JournalLine> journalLines, String METHOD_NAME) { return journalLines.stream() .map(journalLine -> journalLine.METHOD_NAME) .reduce((a, b) -> a.add(b)) .orElse(BigDecimal.ZERO); }
推荐实现方案
不建议使用字符串传方法名+反射的实现方式,这种方案没有编译期校验,方法名写错、返回值不匹配都要到运行时才会抛错,后续重构字段名时字符串参数不会被同步修改,很容易出隐蔽bug,还有额外的反射性能开销。
Java 8及以上版本直接用函数式接口+方法引用实现即可,完全满足消除代码重复的需求,同时保证类型安全:
import java.math.BigDecimal; import java.util.List; import java.util.function.Function; // 通用求和方法 public BigDecimal getTotal(List<JournalLine> journalLines, Function<JournalLine, BigDecimal> amountExtractor) { return journalLines.stream() .map(amountExtractor) .reduce(BigDecimal::add) .orElse(BigDecimal.ZERO); } // 调用方式 BigDecimal totalCcyAmount = getTotal(journalLines, JournalLine::getCcyAmount); BigDecimal totalLclAmount = getTotal(journalLines, JournalLine::getLclAmount); BigDecimal totalRptAmount = getTotal(journalLines, JournalLine::getRptAmount);
如果后续需要对其他实体类的BigDecimal字段求和,还可以把方法改成泛型版本,通用性更强:
public <T> BigDecimal getTotal(List<T> list, Function<T, BigDecimal> amountExtractor) { return list.stream() .map(amountExtractor) .reduce(BigDecimal::add) .orElse(BigDecimal.ZERO); }
反射实现(不推荐)
风险提示:反射实现方案无编译期校验、可维护性差、存在额外性能开销,仅适合必须动态传入方法名字符串的极端场景,常规业务开发优先选择函数式接口方案。
如果确实有动态传入方法名字符串的特殊需求,可以通过反射实现:
import java.lang.reflect.Method; import java.math.BigDecimal; import java.util.List; public BigDecimal getTotalByMethodName(List<JournalLine> journalLines, String methodName) { try { Method getterMethod = JournalLine.class.getMethod(methodName); return journalLines.stream() .map(line -> { try { Object result = getterMethod.invoke(line); if (!(result instanceof BigDecimal)) { throw new IllegalStateException("方法" + methodName + "返回值不是BigDecimal类型"); } return (BigDecimal) result; } catch (Exception e) { throw new RuntimeException("调用字段getter失败", e); } }) .reduce(BigDecimal::add) .orElse(BigDecimal.ZERO); } catch (NoSuchMethodException e) { throw new IllegalArgumentException("JournalLine类不存在无参方法:" + methodName, e); } }
内容的提问来源于stack exchange,提问作者Dimitri Dewaele
相关产品推荐
相关产品推荐

