添加两个可选数字的最优雅方法是什么<BigDecimal>

Whats the most elegant way to add two numbers that are Optional<BigDecimal>

我需要对包裹可选值的两个大小数执行加法运算:

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

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

这是我想出的:

ordersTotal = ordersTotal.flatMap( b -> Optional.of(b.add(newOrder.orElse(BigDecimal.ZERO))));

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

您可以使用可选的流。然后你可以做一个bigdecimals的流,然后减少那些bigdecimals,否则return 0.

这样做的好处是,如果您想执行两个以上的可选操作,则不必更改代码。

(如果需要可以稍后添加代码,目前我没有访问计算机)

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

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

另一个,基于

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

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

此处的 Optional 和 Stream 不能很好地组合在一起。

java8 中最好的是:

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

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

请注意您的解决方案

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

将生成一个空的 Optional,即使 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);

我认为在可选项上使用流或方法链的建议答案非常聪明,但也许聪明到晦涩难懂。 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)));
}

现在,这往往会掩盖双空的特殊情况,这可能不是一个好主意。另一方面,这表示 "add the values if either is non-empty" 这对应用程序可能很有意义。

错的是你的需求不是你的解决方案。一个空的 Optional 不是零而是一个缺失值。你基本上是在问 5 + NaN 等于 5。Optional 的 flatMap 引导你走上快乐的道路:5 + Nan 是 Nan 而这正是 flatMap 所做的。

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

orderTotal = !newOrder.isPresent()? 
             orderTotal : 
             newOrder.flatMap(v -> Optional.of(v.add(orderTotal.orElse(BigDecimal.ZERO));

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

  • 所有闪亮的 Optional 等背后的基本逻辑仍然是
    orderTotal += newOrder

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

  • 如果newOrder还不存在(又是一个空的Optional),则根本不需要任何操作,即不需要修改orderTotal
  • 如果有 newOrder,调用其 flatMap(..),如 maxTrialfire 的原始 post.
  • 中所示

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

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

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

(这可以被认为是 的简化。这也是方法引用无法转换回 lambda 的情况,因为 ordersTotal 不是实际上是最终的)

如果你从这个检查开始,还有另一种可能的“聪明”方法:

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

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