Ricola
Ricola

Reputation: 2922

Method return if Java 8's Optional is present

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

Answers (3)

Holger
Holger

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

slim
slim

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

holi-java
holi-java

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

Related Questions