Search code examples
javaoption-type

Adding two Optionals in a Predicate


I have to write a Predicate in Java where I can query the input to predicate to get two Optional<Integer> objects. I have to return true or false based on the combined value of the two Optionals. Is there some better way to do it other than checking the isPresent() and then get() those values and add.

// Here assuming the comparingValue and integer1 and integer2 are all initialised with values.
// The isGreaterOrEqual function has an implementation.

int comparingValue;
Optional<Integer> integer1;
Optional<Integer> integer2;
if (integer1.isPresent() && integer2.isPresent()) 
    return isGreaterOrEqual(comparingValue, integer1.get() + integer2.get());
if (integer1.isPresent())
    return isGreaterOrEqual(comparingValue, integer1.get());
else if (integer2.isPresent()) 
    return isGreaterOrEqual(comparingValue, integer2.get());
else
    return false;


Solution

  • Here is another approach:

    return Stream.of(integer1, integer2)
        .flatMap(Optional::stream)
        .reduce((a, b) -> a + b)
        .map(t -> isGreaterOrEqual(comparingValue, t))
        .orElse(false);
    

    It is not without using isPresent and get, but it is fluent-style. → Thanks to Hulk's comment, it actually is without isPresent and get.

    What happens here in the abovementioned code, is we build a stream of the two Optional<Integer>s, and then:

    • if both optionals are nonempty, add their values and compare to comparingValue
    • if exactly one of the optionals is nonempty, compare to comparingValue
    • if both are empty, return false.

    The advantage of this approach is that it allows to add more optionals if desired:

    public boolean test(int comparingValue, Optional<Integer> optionalIntegers...) {
        Stream.of(optionalIntegers)
    

    Regarding your comments about 'generalization' — of course, you cannot use the + operator with objects other than String and the wrapper classes of numeric types. If we assume your class has a merge method with the following signature:

    Money add(Money m1)
    

    then you only need to replace the line with reduce to the following:

    .reduce((a, b) -> a.add(b))
    

    or just

    .reduce(Money::add)
    

    Java 8

    For Java 8, you could replace .flatMap(Optional::stream) by

    .filter(Optional::isPresent)
    .map(Optional::get)
    

    and you're good to go.