Deploying Multiple Optional Variables in Java 8

I have the following problem. Let's say you have 2 Optional variables

 Optional<Contact> c1 = ... Optional<Contact> c2 = ... 

and a method that requires 2 variables of type Contact

 void match(Contact c1, Contact c2) {...} 

and you need to deploy both c1 and c2 Optional vars and pass them to the match() method.

My question is "What is the most elegant way to do this in Java 8?"

So far I have found 2 ways:

  • using isPresent

     if (c1.isPresent() && c2.isPresent()) { match(c1.get(), c2.get()); } 
  • using nested ifPresent

     c1.ifPresent((Contact _c1) -> { c2.ifPresent((Contact _c2) -> { match(_c1, _c2); }); }); 

Both ways are terrible, in my opinion. In Scala, I can do this:

 for { contact1 <- c1 contact2 <- c2 } yield { match(contact1, contact2); } 

Is there a way in Java 8 to make this tidier than the above?

+12
java scala optional
source share
3 answers

The solution you provided in Scala is just syntactic sugar for using flatMaps internally. You can also use flatmaps in Java 8 (but there is no syntactic sugar for it).

 c1.flatMap(contact1 -> c2.flatMap(contact2 -> match(contact1, contact2))); 

this is almost the same as solution 2 that you provided. You can also use the applicative functor from https://github.com/aol/cyclops-react (I am one of the authors) or any other Java 8 functional library.

Applicative functor

 Optional<String> o3 = Maybe.fromOptional(o1).ap2(String::concat).ap(o2).toOptional(); 

For understanding,

 Do.add(o1) .add(o2) .yield(a->b->a.concat(b)); 
+6
source share

You can turn off Scala understanding for map / flatMap in Java 8 with this function:

 public static <T,G,V> Optional<V> map2(Optional<T> opt1, Optional<G> opt2, BiFunction<T, G, V> f) { Optional<V> result = opt1.flatMap(t1 -> opt2.map(t2 -> f.apply(t1, t2))); return result; } 

And then pass your matching function

+5
source share

If you consider arguments that have no value as an exception, you can handle them, for example:

 try { match(c1.orElseThrow(NoVal::new), c2.orElseThrow(NoVal::new)); } catch (NoVal ex) { ... } 

If they are not an exceptional case, I would go with your first option as more specific regarding your intentions. In my opinion, it is quite understandable and readable and is easily replaced with the use of orElse if you want to switch to using the default values ​​if the options are empty.

0
source share

All Articles