使用函数式接口重构 Java8 代码

问题描述

输入:

public BigDecimal getMaxValuation(ServiceData data) {
    System.out.println("getMaxValuation()");
    BigDecimal calculatedamount;
    //4 String returnValue = getReturnValue(data);
    Function<ServiceData,String> returnValueFn = this::getReturnValue;
    BigDecimal orderSize = getorderSize(returnValueFn.apply(data),60);
    Predicate<String> gasPredicate = "GAS"::equalsIgnoreCase;
    Predicate<String> oilPredicate = "OIL"::equalsIgnoreCase;
    if(gasPredicate.test(returnValueFn.apply(data)))
        calculatedamount = callA(data.getValuation())
    else if(oilPredicate.test(returnValueFn.apply(data)))
        calculatedamount = callB(data.getValuation())
    else
        calculatedamount = callC(data.getValuation())
    return calculatedamount;
}

public String getReturnValue(ServiceData data){
    System.out.println("getReturnValue()");
    return returnValue;
}   

在上面的函数 getMaxValuation() 中,当我们注释第 4 行并将其替换为 Function<ServiceData,String> 时, getReturnValue() 在执行过程中被调用了 3 次。但是当我们取消注释第 4 行并删除所有 Function<ServiceData,String> 相关更改 getReturnValue() 仅被调用一次。

当我们使用 Function 时,有什么方法可以实现相同的行为吗?

解决方法

无论是直接调用方法还是使用函数式接口,调用一次并将结果存储在局部变量中以避免重复评估的逻辑都不会改变。

到目前为止,您将直接调用重写为函数式接口的使用本身看起来像是结束了,实际上并没有改进任何东西,而只是使代码更加复杂。

使用函数式编程改进代码的一种方法是使用函数映射通过一次查找替换 if-else 阶梯:

static final Map<String,Function<Valuation,BigDecimal>> METHOD;
static {
    Map<String,BigDecimal>> m
        = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
    m.put("GAS",ContainingClass::callA);
    m.put("OIL",ContainingClass::callB);
    METHOD = Collections.unmodifiableMap(m);
}

public BigDecimal getMaxValuation(ServiceData data) {
    // don't know how to incorporate this,as it was entirely unused
    // BigDecimal orderSize = getOrderSize(getReturnValue(data),60);

    return METHOD.getOrDefault(getReturnValue(data),ContainingClass::callC)
        .apply(data.getValuation());
}

其中Valuation是指ServiceData.getValuation()的返回类型,ContainingClasscallAcallBcallC的声明类,假设 static 方法。

如果这些方法不是static,则代码必须看起来像

static final Map<String,BiFunction<ContainingClass,Valuation,ContainingClass::callB);
    METHOD = Collections.unmodifiableMap(m);
}

public BigDecimal getMaxValuation(ServiceData data) {
    return METHOD.getOrDefault(getReturnValue(data),ContainingClass::callC)
        .apply(this,data.getValuation());
}