什么是最优雅的方式来添加两个可选的<bigdecimal>数字

时间:2016-09-14 19:35:29

标签: java functional-programming java-8 optional

我需要对包含选项的两个大小数执行添加操作:

var myURL: URL = NSURLComponents().url!

我想实现ordersTotal + = newOrder 重要的是要注意,如果两个值都为空,则结果同样应为空(即不为零)。

以下是我提出的建议:

Optional<BigDecimal> ordersTotal;
Optional<BigDecimal> newOrder;

但我想知道是否有更优雅的解决方案。

8 个答案:

答案 0 :(得分:12)

我认为在选项上使用流或方法链的建议答案非常聪明,但也许是如此模糊以至于模糊不清。 OP已将其建模为ordersTotal += newOrder,但如果两者都为空,则结果应为空而不是零。也许编写代码是合理的,因为它说:

    if (!ordersTotal.isPresent() && !newOrder.isPresent()) {
        result = Optional.empty();
    } else {
        result = Optional.of(ordersTotal.orElse(ZERO).add(newOrder.orElse(ZERO)));
    }

虽然这不是最短的,但它清楚地表达了OP要求的内容。

现在我已将计算值分配给result,但OP实际上想将其分配回ordersTotal。如果我们都知道它们都是空的,那么我们就可以跳过将空指定给ordersTotal的then子句。这样做,然后反转条件会更简单:

    if (ordersTotal.isPresent() || newOrder.isPresent()) {
        ordersTotal = Optional.of(ordersTotal.orElse(ZERO).add(newOrder.orElse(ZERO)));
    }

现在,这往往会掩盖两个空的特殊情况,这可能不是一个好主意。另一方面,这表示&#34;如果其中任何一个非空,则添加值#34;这可能对应用程序有很大意义。

答案 1 :(得分:7)

不确定你是否会认为它更优雅,但这里有一个替代方案:

ordersTotal = Optional.of(ordersTotal.orElse(BigDecimal.ZERO).add(newOrder.orElse(BigDecimal.ZERO)));

另一个,基于@user140547's suggestion

ordersTotal = Stream.of(ordersTotal, newOrder)
        .filter(Optional::isPresent)
        .map(Optional::get)
        .reduce(BigDecimal::add);

请注意,即使两个选项都为空,第一个版本也会返回Optional.of(BigDecimal.ZERO),而在这种情况下,第二个版本将返回Optional.empty()

答案 2 :(得分:1)

您可以使用选项流。然后你可以创建一个bigdecimals流,然后减少那些bigdecimals,否则返回0。

这样做的好处是,如果您想要执行两个以上的选项,则无需更改代码。

(如果需要,可以在以后添加代码,目前我无法访问计算机)

答案 3 :(得分:1)

请注意您的解决方案

ordersTotal=ordersTotal.flatMap(b -> Optional.of(b.add(newOrder.orElse(BigDecimal.ZERO))));
如果Optional为空,

将生成空ordersTotal,即使newOrder不是。

可以通过将其更改为

来解决此问题
ordersTotal=ordersTotal
    .map(b -> Optional.of(b.add(newOrder.orElse(BigDecimal.ZERO))))
    .orElse(newOrder);

但我更喜欢

ordersTotal=ordersTotal
    .map(b -> newOrder.map(b::add).orElse(b))
    .map(Optional::of).orElse(newOrder);

答案 4 :(得分:1)

我知道这是一个老线程,但是这个怎么样?

,SUM(CASE WHEN ReceiptCode LIKE 'V.Ch%' THEN ReceiptAmount ELSE 0 END) 
 OVER (Partition by Receipt) 
   AS ChangeSpend

我对这种方法背后的想法是这样的:

  • 在所有灵魂可选等的背后,这里的基本逻辑仍然是 orderTotal + = newOrder

  • 在第一个newOrder存在之前,orderTotal不存在,在代码中由空的Optional表示。

  • 如果newOrder尚不存在(另一个空可选),则根本不需要任何操作,即无需修改orderTotal
  • 如果有newOrder,请调用maxTrialfire原始帖子中提供的flatMap(..)。

答案 5 :(得分:0)

这里的Optional和Stream不能很好地融合在一起。

java 8中最好的是:

ordersTotaI = !ordersTotaI.isPresent() ? newOrder
         : !newOrder.isPresent() ? ordersTotaI
         : Optional.of(ordersTotaI.get().add(newOrder.get()));

然而,好消息是,java 9会为Optional添加一些不错的(也是丑陋的)功能。

答案 6 :(得分:0)

您的要求不是您的解决方案。空的Optional不是零,而是缺少值。您基本上要求5 + NaN等于5.可选的flatMap引导您走向快乐的道路:5 + Nan是Nan,这正是flatMap所做的。

答案 7 :(得分:0)

考虑到您想重新分配给ordersTotal,您会注意到ordersTotal仅在存在newOrder时才会更改。

因此,您可以从该支票开始,并将其写为:

if (newOrder.isPresent()) {
    ordersTotal = newOrder.map(ordersTotal.orElse(ZERO)::add);
}

(这可以看作是Stuart Marks' second solution的简化。在这种情况下,由于ordersTotal并不是有效的最终形式,方法引用也不能转换回lambda)

如果通过此检查开始,还有另一种可能的“ 聪明”方法:

if (newOrder.isPresent()) {
    ordersTotal = ordersTotal.map(o -> newOrder.map(o::add)).orElse(newOrder);
}

其中中间map()返回一个Optional<Optional<BigDecimal>>,其内部Optional不能为空。由于可读性差,我不认为它是一个好的解决方案,因此,我建议选择第一个选项或Stuart的解决方案之一。