Consider the following use case of an application with a user preference system:
We want to get the bool value of the preference MyFlag
.
In the best case we want it from the settings of the current user.
If that fails, we want to get MyFlag
from the default settings.
If even that fails, throw.
The settings are on a server. This connection is slow and can fail.
Fetching the settings and getting the prefernce can all fail, too.
So lets use javas Optionals:
public static boolean getMyFlag()throws NoSuchElementException
{
return getUserOrDefaultPreference("MY_FLAG");
}
private Boolean getUserOrDefaultPreference(String preferenceName) throws NoSuchElementException
{
return Optional.ofNullable(getConnection()) // get slow connection
.map(connection -> connection.getUserSettings()) // get user settings
.map(settings -> settings.getPref(preferenceName)) // return preference
.orElse(slowlyGetDefaultPreference(preferenceName)); // or else return default value
}
private Boolean slowlyGetDefaultPreference(String preferenceName) throws NoSuchElementException
{
return Optional.ofNullable(getConnection()) // get slow connection
.map(connection -> connection.getDefaultSettings()) // get default settings
.map(settings -> settings.getPref(preferenceName)) // return preference
.orElseThrow(() -> new NoSuchElementException()); // if any of the above fails throw
}
Problem here is that the connection can be very slow. When .orElse(slowlyGet...);
is called the func slowlyGetDefaultPreference()
is evaluated first, regardless, if my optional is empty or has a value. This is a performance penalty, I have to avoid.
I tried using a supplier via .orElseGet(() -> slowlytGet..)
, but that resulted in the same problem.
So my only recourse is the ugly isPresent()
anti-pattern, which from a readability standpoint ruins the whole Optional flow:
private Boolean getUserOrDefaultPreference(String preferenceName) throws NoSuchElementException
{
Optional<Boolean> opt = Optional.ofNullable(getConnection())
.map(connection -> connection.getUserSettings())
.map(settings -> settings.getPref(preferenceName));
if(opt.isPresent())
{
return opt.get();
}
else
{
slowlyGetDefaultPreference(preferenceName));
}
}
The same holds for throwing the exception, as I do not want to pay the cost of constructing one.
Am I missing something here or is this the only solution?
orElse(): returns the value if present, otherwise returns other. orElseGet(): returns the value if present, otherwise invokes other and returns the result of its invocation.
The orElse() method of java. util. Optional class in Java is used to get the value of this Optional instance, if present. If there is no value present in this Optional instance, then this method returns the specified value.
What is the ofNullable() method of the Optional class? The ofNullable() method is used to get an instance of the Optional class with a specified value. If the value is null , then an empty Optional object is returned.
The difference between orElse()
and orElseGet(lambdas)
is, the first function is always called, but orElseGet
is called, when some obejct before orElseGet
in Optional.ofNullable()
was null.
It means, if you don't want call every time method from orElse()
, you must useorElseGet()
In your example, for your methodslowlyGetDefaultPreference(preferenceName)
please use orElseGet()
return Optional.ofNullable(getConnection()) // get slow connection
.map(connection -> connection.getUserSettings()) // get user settings
.map(settings -> settings.getPref(preferenceName)) // return preference
.orElseGet(() -> slowlyGetDefaultPreference(preferenceName));
Then you code not evaluate last method, when all object in map function is not null
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