So I'm working with Optionals and came across a strange behaviour. I want to know if this is really an intendet "feature" or something...odd...
Here is the given example: I've got a method with an Optional in whose orElse I want to evaluate an other optional. If the other Optional is not present, I'll raise an IllegalArgumentException:
firstOptionalVar.orElse(secondOptionalVar.orElseThrow(IllegalArgumentException::new));
Now if the secondOptionalVar
is an empty Optional, it will raise an IllegalArgumentException, even if the firstOptionalVar
is Present. This doesn't seem right to me. I would expect it to just raise an IllegalArgumentException if the firstOptionalVar
would not be present.
It's not a big deal to get arround this behavior with java7-methods like:
firstOptionalVar.isPresent() ? firstOptionalVar.get() : secondOptionalVar.orElseThrow(IllegalArgumentException::new);
Has anyone else experienced this behaviour before? Is this really the way optionals should behave?
This is the intended behavior. orElse
expects an argument of type T
(whatever the generic type of the Optional
is. orElseThrow
returns a T
, so it needs to be evaluated first, in order to pass the parameter into orElse
.
What you want is orElseGet
, which takes a Supplier<T>
. That will delay execution of the orElseThrow
until after firstOptionalVar
has already been checked.
So your code should look like this:
firstOptionalVar.orElseGet(() -> secondOptionalVar.orElseThrow(IllegalArgumentException::new));
That will turn the orElseThrow
section into a lambda, and only evaluate it if it's needed (ie. when firstOptionalVar
doesn't have a value to get).
At the heart of everything, you're still only calling Java methods, and therefore the standard Java order of evaluation applies.
Specifically: secondOptionalVar.orElseThrow(IllegalArgumentException::new)
has to be evaluated before the call to firstOptionalVar.orElse()
, as it provides the argument of firstOptionalVar.orElse()
.
I can't see any easy way of resolving this with standard methods of Optional
, you can always build a stream out of the two optionals and get the first element or else throw an exception, but it seems a bit convoluted.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With