javajava-streamoption-type

Logical OR of multiple Optional instances?


Is there a more elegant way to represent the following algorithm using the Java core API?

Optional<Float> input = Optional.of(A);
Optional<Float> output = input.map(function1).map(function2);
if (!output.isPresent())
{
  output = input.map(function1).map(function2);
  if (!output.isPresent())
  {
    output = input.map(function3).map(function4);
      if (!output.isPresent())
        output = input.map(function5).map(function6);
  }
}

That is, instead of nesting multiple invocations of isPresent() I'd like to invoke a method like map() but that would only get invoked if a value is absent.

In an ideal world, I want to invoke something along the lines of:

Optional<Float> output = input.or(input.map(function1).map(function2), input.map(function2).map(function3), input.map(function4).map(function5));

This is what I've got so far, but can we do better?

public class Optionals
{
    /**
     * @param <T>       the type of value returned by the Optionals
     * @param suppliers one or more suppliers that return a supplier
     * @return the first Optional that {@link Optional#isPresent() isPresent()}
     */
    public static <T> Optional<T> or(Supplier<Optional<T>>... suppliers)
    {
        for (Supplier<Optional<T>> supplier: suppliers)
        {
            Optional<T> candidate = supplier.get();
            if (candidate.isPresent())
                return candidate;
        }
        return Optional.empty();
    }

    /**
     * Prevent construction.
     */
    private Optionals()
    {
    }
}

Solution

  • I don't like the Supplier scheme to much in this case and would probably prefer this approach:

    public static void main(String[] args) {
    
        Float x = 1.0f;
        final Optional<Float> input = Optional.of(x);
        Optional<Float> output = firstPresent(input, (a) -> a.map(FUNCTION1).map(FUNCTION2),
                                         (a) -> a.map(FUNCTION3).map(FUNCTION4),
                                         (a) -> a.map(FUNCTION5).map(FUNCTION6));
    }
    
    @SafeVarargs
    public static <I,O> Optional<O> firstPresent(Optional<I> input, Function<Optional<I>, Optional<O>>... functions) {
        for (Function<Optional<I>, Optional<O>> function : functions) {
            final Optional<O> output = function.apply(input);
            if(output.isPresent()) {
                return output;
            }
        }
        return Optional.empty();
    }