descript
descript

Reputation: 99

What is intended behavior of Guava's Optional.or()?

A method where I chain optionals does not behave how I thought it would from reading the docs.

Assume all function_n return an Optional<Foo>

public Foo getFooFromService() {
     return this.function_1()
         .or(this.function_2())
         .or(this.function_3())
         .or(DEFAULT_VAL)

I thought that for the above code, if function_1 returned a non-absent Optional, then the program would return the inner value of it (the result of .get()) and not do any further computation on function_2 and function_3

My program is for sure doing that additional computation

Upvotes: 2

Views: 329

Answers (2)

Olivier Gr&#233;goire
Olivier Gr&#233;goire

Reputation: 35457

Guava "gently recommends" to use Java's Optional

So use Java's Optional to write the rather legible code:

import java.util.*;
class Main {
  public static void main(String[] args) {
    new Main().getFooFromService();
  }
  String getFooFromService() {
    return this.function_1()
      .or(this::function_2) // Requires Java 9
      .or(this::function_3) // Requires Java 9
      .orElse("DEFAULT_VALUE");
  }

  Optional<String> function_1() {
    System.out.println("function_1 called");
    return Optional.empty();
  }

  Optional<String> function_2() {
    System.out.println("function_2 called");
    return Optional.of("b");
  }

  Optional<String> function_3() {
    System.out.println("function_3 called");
    return Optional.of("c");
  }
}

You'll see that in this case, with the given setup, function_1 and function_2 are called, but not function_3.

Upvotes: 2

Andrew
Andrew

Reputation: 49646

In order to return a value from getFooFromService, function_1 and three ors have to be executed, meaning that their parameters will be evaluated. function_2 and function_3 will be run under any circumstances.

The option that might be suitable for you is the overloaded version that takes a Supplier which implies lazy evaluation.

public abstract T or(Supplier<? extends T> supplier)

UPDATE

It's a @Beta method (a subject to change), and I find it entirely useless. It resolves a Supplier<? extend T> to T, thus ruins the opportunity of building a chain. Basically, you can't rewrite your snippet to use this method.

UPDATE 1

But you could switch to Java's Optional and write

return function_1()
       .orElseGet(() -> function_2()
                        .orElseGet(() -> function_3()
                                         .orElse(DEFAULT_VAL)));

which isn't that expressive, but working as expected.

My formatting is awful, but you get the idea ;)

Upvotes: 5

Related Questions