Reputation: 2922
In Java 8, is there a way to force a method return if an Optional
is present?
I would guess that the answer is no. But then how to avoid using isPresent()
and get()
in the following code?
public Bar myMethod(){
if(condition){
Optional<Foo> foo = getOptionalFoo();
if(foo.isPresent()){
return foo.get().getBar();
}
}
return getOtherBar();
}
One solution would be to write :
public Bar myMethod(){
if(condition){
return getOptionalFoo()
.map(Foo::getBar)
.orElseGet(() -> getOtherBar());
}
return getOtherBar();
}
But it doesn't seem like a good idea because now we write twice the call to getOtherBar
. Is there a way to avoid this?
EDIT: I also thought of
public Bar myMethod(){
//Or a ternary operator but there are hard to read when they make long lines
Optional<Foo> foo;
if(condition){
foo = getOptionalFoo();
} else {
foo = Optional.empty();
}
return foo.map(Foo::getBar)
.orElseGet(() -> getOtherBar());
}
But the issue here is that we create a useless empty Optional
and chain methods to it. If the condition is not met 99% of the time, this make it quite less efficient than the original code (especially if we have several map()
and filter()
).
NOTE: I gave a basic example, in reality in our code we have more conditions that depend on the results of other calls (which depend of foo).
Upvotes: 1
Views: 4001
Reputation: 298143
What is condition
? Since it’s located in an instance method, it must be a condition depending on the state of the instance. For simplicity, assume that this is a field of your class, then you could write
public Bar myMethod() {
return Optional.of(this)
.filter(obj -> obj.condition)
.flatMap(ClassContainingMyMethod::getFoo)
.map(Foo::getBar)
.orElseGet(this::getOtherBar);
}
If getFoo()
is not expensive and has no side effects, you could accept calling it always, even if you’re not going to use its result, depending on condition
. Which would simplify the code to
public Bar myMethod() {
return getFoo()
.filter(foo -> condition)
.map(Foo::getBar)
.orElseGet(this::getOtherBar);
}
Note that there is a semantic difference to your original code. When getBar
returns null
, the original code returns null
, whereas all code using Optional.map
with a function returning the result of getBar()
will fall back to calling getOtherBar()
.
Upvotes: 3
Reputation: 41223
This falls together pretty easily once you get into the Optional
mindset, and use a ternary operator to handle your condition.
public Bar myMethod(){
return ( condition ? getOptionalFoo() : Optional.<Foo> empty())
.map(f -> f.getBar())
.orElseGet(() -> getOtherBar())
}
Upvotes: 2
Reputation: 30676
How about this? the form of the code below is more readable but it is slower than yours, since if the condition
is not satisfied, getOtherBar()
also need additional operations (map
& orElseGet
) on an Optional
.
public Bar myMethod() {
Optional<Foo> source = condition ? getFoo() : Optional.empty();
return source.map(Foo::getBar)
.orElseGet(this::getOtherBar);
}
However, if there are a lot of duplications likes as above, then you can free yourself by extract a method, for example:
private Optional<Foo> foo(){
return condition ? getFoo() : Optional.empty();
}
public Bar myMethod() {
return foo().map(Foo::getBar).orElseGet(this::getOtherBar);
}
public Other otherMethod() {
return foo().map(Foo::getOther).orElseGet(this::getOther);
}
Upvotes: 3