Optional<T> (since Java 8) is a container that may or may not hold a non-null value. It helps express absent values explicitly and reduces null-related bugs.

Creating Optional

  Optional<String> present = Optional.of("Hello");
Optional<String> empty = Optional.empty();
Optional<String> nullable = Optional.ofNullable(getName()); // null-safe
  
Method Behavior if null
Optional.of(value) Throws NullPointerException
Optional.ofNullable(value) Returns Optional.empty()
Optional.empty() Returns empty Optional

Checking and Retrieving Values

  Optional<String> opt = Optional.of("Java");

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

// Preferred — avoids explicit isPresent/get
opt.ifPresent(System.out::println);

String value = opt.orElse("default");
String value2 = opt.orElseGet(() -> expensiveDefault());
String value3 = opt.orElseThrow(() -> new IllegalStateException("Not found"));
  

Transforming Optional

  Optional<String> name = Optional.of("  alice  ");

Optional<String> trimmed = name.map(String::trim);
Optional<Integer> length = name.map(String::length);

Optional<String> upper = name
    .filter(s -> !s.isBlank())
    .map(String::trim)
    .map(String::toUpperCase);
  

flatMap

Use flatMap when the mapping function returns an Optional:

  public Optional<User> findUserById(Long id) { ... }

Optional<String> email = findUserById(1L)
    .flatMap(User::getEmail); // User.getEmail() returns Optional<String>
  

Optional in Method Returns

  public Optional<User> findByEmail(String email) {
    User user = database.lookup(email);
    return Optional.ofNullable(user);
}

// Caller
findByEmail("[email protected]")
    .ifPresent(user -> sendWelcomeEmail(user));
  

Optional Chains (Java 9+)

  Optional<String> result = findUser(id)
    .flatMap(User::getAddress)
    .flatMap(Address::getCity)
    .map(String::toUpperCase);

// Java 9: ifPresentOrElse
opt.ifPresentOrElse(
    value -> System.out.println("Found: " + value),
    () -> System.out.println("Not found")
);

// Java 10: orElseThrow() with no argument
String name = opt.orElseThrow(); // throws NoSuchElementException
  

Anti-Patterns — Do NOT

  // ❌ Don't use Optional as a field
public class User {
    private Optional<String> middleName; // Bad
}

// ❌ Don't use Optional as a method parameter
public void process(Optional<String> name) { } // Bad

// ❌ Don't call get() without checking
String s = opt.get(); // throws if empty

// ❌ Don't use Optional for collections — use empty collections instead
Optional<List<String>> items; // Bad — return List.of() instead
  

Best Practices

  • Use Optional as a return type to signal “may not exist”
  • Never use Optional for fields or method parameters
  • Prefer orElseGet() over orElse() when the default is expensive to compute
  • Use map/flatMap/filter to chain operations without null checks
  • Return Optional.empty() instead of returning null