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

后端 未结 8 822
既然无缘
既然无缘 2021-02-08 03:29

I need to perform an add operation on two big decimals that are wrapped optionals:

Optional ordersTotal;
Optional newOrder;
<         


        
相关标签:
8条回答
  • 2021-02-08 03:42

    Not sure if you'll consider it more elegant, but here's one alternative:

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

    Another, based on @user140547's suggestion:

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

    Note that the first version returns Optional.of(BigDecimal.ZERO) even when both optionals are empty, whereas the second will return Optional.empty() in such a case.

    0 讨论(0)
  • 2021-02-08 03:42

    I think the suggested answers of using streams or chains of methods on optionals are very clever, but perhaps so clever as to be obscure. The OP has modeled this as ordersTotal += newOrder with the exception that if both are empty, the result should be empty instead of zero. Maybe it would be reasonable to write the code so that it says that:

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

    While this isn't the shortest, it clearly expresses exactly what the OP asked for.

    Now I've assigned the computed value to result but the OP actually wanted to assign it back to ordersTotal. If we know both are empty, we can then skip the then-clause that assigns empty to ordersTotal. Doing that, and then inverting the condition gives something simpler:

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

    Now, this tends to obscure the both-empty special case, which might not be a good idea. On the other hand, this says "add the values if either is non-empty" which might make a lot of sense for the application.

    0 讨论(0)
  • 2021-02-08 03:45

    What is wrong is your requirement not your solution. An empty Optional is not zero but a missing value. You're basically asking that 5 + NaN is equal to 5. Optional's flatMap guides you to the happy path: 5 + Nan is Nan and this is exactly what flatMap does.

    0 讨论(0)
  • 2021-02-08 03:46

    Note that your solution

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

    will produce an empty Optional, if ordersTotal is empty, even if newOrder is not.

    This could be fixed by changing it to

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

    but I’d prefer

    ordersTotal=ordersTotal
        .map(b -> newOrder.map(b::add).orElse(b))
        .map(Optional::of).orElse(newOrder);
    
    0 讨论(0)
  • 2021-02-08 03:54

    You could use a stream of optionals. Then you can make a stream of bigdecimals, and then reduce those bigdecimals, or else return 0.

    This has the advantage that you don't have to change the code if you want to do that more than two optionals.

    (code can be added later if needed, currently I don't have access to a computer)

    0 讨论(0)
  • 2021-02-08 03:55

    I know this is an old thread, but how about this?

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

    My thinking behind this approach is like this:

    • Behind all the shinny Optional etc. the basic logic here is still
      orderTotal += newOrder

    • Before the first newOrder exists orderTotal does not exist, which is represented by an empty Optional in the code.

    • If a newOrder does not exist yet (another empty Optional), no operation is necessary at all, i.e. there is no need to modify orderTotal
    • If there is a newOrder, invoke its flatMap(..) as presented in maxTrialfire's original post.
    0 讨论(0)
提交回复
热议问题