Calling different methods based on values of two Optionals

  • A+
Category:Languages

While working with Java 8 Optionals I face following scenario very frequently. I have two Optional objects and then I want to call different methods based on the values (ifPresent) of those Optionals.

Here is an example:

void example(Optional<String> o1, Optional<String> o2) throws Exception {     if (o1.isPresent() && o2.isPresent()) {        handler1(o1.get(), o2.get());     } else if (o1.isPresent()) {        handler2(o1.get());     } else if (o2.isPresent()) {        handler3(o2.get());     } else {        throw new Exception();     } } 

However, this chain of if-else statements doesn't seem like a proper way of working with Optional (after all, they were added so that you can avoid writing these if-else checks everywhere in your code).

What is the proper way of doing this with Optional objects?

 


You said that you use such structure frequently, so I propose to introduce a Helper class:

final class BiOptionalHelper<F, S> {     private final Optional<F> first;     private final Optional<S> second;      public BiOptionalHelper(Optional<F> first, Optional<S> second){         this.first = first;         this.second = second;     }      public BiOptionalHelper<F, S> ifFirstPresent(Consumer<? super F> ifPresent){         if (!second.isPresent()) {             first.ifPresent(ifPresent);         }         return this;     }      public BiOptionalHelper<F, S> ifSecondPresent(Consumer<? super S> ifPresent){         if (!first.isPresent()) {             second.ifPresent(ifPresent);         }         return this;     }      public BiOptionalHelper<F, S> ifBothPresent(BiConsumer<? super F, ? super S> ifPresent){         if(first.isPresent() && second.isPresent()){             ifPresent.accept(first.get(), second.get());         }         return this;     }      public <T extends Throwable> void orElseThrow(Supplier<? extends T> exProvider) throws T{         if(!first.isPresent() && !second.isPresent()){             throw exProvider.get();         }     } } 

Which then may be used in a way like this:

new BiOptionalHelper<>(o1, o2)     .ifBothPresent(this::handler1)     .ifFirstPresent(this::handler2)     .ifSecondPresent(this::handler3)     .orElseThrow(Exception::new); 

Though, this just moves your problem into a separate class.

Note: above code may be refactored to not use Optional and isPresent() checks at all. And just use null for first and second and replace isPresent() with null-checks.

As it is generally a bad design to store Optional in fields or accept them as parameters in the first place. As JB Nizet already pointed out in a comment to the question.


Another way it to move that logic into common helper method:

public static <F, S, T extends Throwable> void handle(Optional<F> first, Optional<S> second,                                                        BiConsumer<F, S> bothPresent, Consumer<F> firstPresent,                                                        Consumer<S> secondPresent, Supplier<T> provider) throws T{     if(first.isPresent() && second.isPresent()){         bothPresent.accept(first.get(), second.get());     } else if(first.isPresent()){         firstPresent.accept(first.get());     } else if(second.isPresent()){         secondPresent.accept(second.get());     } else{         throw provider.get();     } } 

Which then could be called like this:

handle(o1, o2, this::handler1, this::handler2, this::handler3, Exception::new); 

But it's still kind of messy to be honest.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: