Java Optional

Optional<T> makes "this might be absent" explicit in the type, instead of hiding it behind a null the caller forgets to check.

Java
import java.util.Optional;

public Optional<Book> findById(int id) {
    Book b = database.get(id);
    return Optional.ofNullable(b);
}

Creating

Java
Optional<String> a = Optional.of("value");        // throws if null
Optional<String> b = Optional.ofNullable(maybe);  // null-safe
Optional<String> c = Optional.empty();

Consuming

Java
Optional<Book> found = findById(5);

if (found.isPresent()) {
    System.out.println(found.get().getTitle());
}

found.ifPresent(book -> System.out.println(book.getTitle()));

found.ifPresentOrElse(
    book -> System.out.println(book.getTitle()),
    ()   -> System.out.println("Not found")
);

Book book = found.orElse(new Book("Unknown", 0));
Book book2 = found.orElseGet(() -> loadDefault());   // lazy — only runs if empty
Book book3 = found.orElseThrow(() -> new NotFoundException("No book " + id));

Prefer orElseGet over orElse when the fallback is expensive: orElse's argument is evaluated always, even when the Optional has a value.

Chaining

Java
String city = findUser(id)
    .map(User::getAddress)
    .map(Address::getCity)
    .orElse("Unknown");

Each map is skipped if the Optional is empty — no nested null checks.

Rules of use

Do return Optional from methods that may find nothing.

Don't:

  • Use it for fields or method parameters — it isn't serializable and adds noise.
  • Call .get() without checking; that just moves the NPE.
  • Return Optional<List<T>> — return an empty list instead.
  • Use it for collections at all: an empty collection already means "nothing".
Java
// good
Optional<User> findByEmail(String email);

// bad
void save(Optional<User> user);     // just accept User
List<Book> getBooks();               // return empty list, never Optional