Lazy evaluation of Optional
Asked Answered
G

3

11

Is it possibile to have an java.util.Optional which is evaluated only if needed?

I need to pass an Optional to a method (of an API that I cannot change), and this method may or may not make use the value of that Optional. Since the value is computed by a heavy operation, I'd like to compute that value only when (and if) it is needed, e.g. calling get(), orElseGet(), ifPresent(), etc.

Something like Optional.ofLazy(Supplier<T> computeValue).

Gulch answered 1/6, 2017 at 14:59 Comment(3)
Assuming you're talking about java.util.Optional (not Guava) - Optional is final, and doesn't already expose such behaviour. So there's no way to achieve this without modifying your API.Armenian
@OliverCharlesworth Your comment headed me to the right direction: inspecting the source code of java.util.Optional it's pretty clear this behavior is not supported in Java 8 nor 9 (yet).Gulch
@GiovanniLovato - Oh! I just looked at the Javadoc :)Armenian
P
6

What you want is a Supplier which returns an Optional. Supplier makes the lazy part.

Conceptual code:

Foo heavyComputation() { ... }

void main() {
    Supplier<Optional<Foo>> sup = () -> heavyComputation();
    doSomethingWhichMayNeedHeavyResult(sup);
}

void doSomethingWhichMayNeedHeavyResult(Supplier<Optional<Foo>> sup) {
    if (electricityIsTooCheap) {
       Foo foo = sup.get().get().orElse(null); // This will lazy load.
    }
}

For brevity, I would also like to have something which combines both into one, but that will come later I guess.

Poniard answered 9/5, 2019 at 14:37 Comment(2)
+1. It may also be wise to decorate the Supplier with some logic to cache and reuse the result if its get method is invoked multiple times.Selfannihilation
May be worth mentioning that probably in most use cases, due to the lazy evaluation, Supplier<Foo> will suffice. If you need a Foo then you're unlikely to be able to proceed without one ...Delarosa
I
0

I think the answer above has it almost right, but I would just encapsulate a Supplier inside of an Optional. Then specify in your documentation that the call to Supplier.get() actually performs the expensive operation.

For example, this is in my code:

<T> Optional<Supplier<T>> findInstance();

When used, I can just check if it exists by doing this:

if (findInstance().isPressent()) { /* do something */ }

No call to Supplier.get() therefore we don't perform the heavy operation.

We can also do something like this, to actually perform the operation conditionally:

findInstance().ifPresent(supplier -> supplier.get().foo());

That will perform the heavy operation explicitly when you call "supplier.get()"

For the sake of clarity, you could create a special interface w/ a JavaDoc tag to make it abundantly clear:

/**
 * An on-demand loader which will read objects from disk.
 **/
@FunctionalInterface
public interface Loader<T> { 
    /**
     * Returns an newly-loaded instance of T from disk.
     **/
    T load(); 
}

This way you explicitly name the method "load" making the intent a little more clear.

Itch answered 30/8 at 17:21 Comment(0)
E
-1

In the name of brevity, I think orElseGet is what you're after:

void foo() {
    System.out.println("Prints only foo");
    System.out.println(Optional.of("foo").orElseGet(() -> other()));
    System.out.println("Prints foo and bar");
    System.out.println(Optional.of("foo").orElse(other()));
}
private String other() {
    System.out.println("bar");
    return "bar";
}
Epoxy answered 17/3, 2023 at 7:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.