Skip to content

Optional — Creation, map/flatMap, orElse, ifPresent, and Best Practices

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Optional. We cover key concepts, practical examples, and best practices to help you master this topic.

Java Optional is a container object that may or may not contain a non-null value, providing a functional alternative to null checks. Optional<T> forces you to think about the absent case — you cannot simply get the value without explicitly handling the possibility of emptiness.

What You'll Learn

  • Creating Optional: empty, of, ofNullable
  • Retrieving values: get, orElse, orElseGet, orElseThrow
  • Functional operations: map, flatMap, filter, ifPresent
  • Best practices and common misuses

Why It Matters

Null references are the source of countless NullPointerExceptions. While Optional does not eliminate null (it is still possible to have a null reference), it makes the absence of a value explicit in the type system and encourages proper handling.

Real-World Use

Spring Data JPA returns Optional from Repository methods. CompletableFuture uses Optional-like patterns. Stream terminal operations like findFirst() return Optional.


Creating Optional

// Empty Optional
Optional<String> empty = Optional.empty();

// Non-null value
Optional<String> value = Optional.of("Hello");
// Throws NullPointerException if argument is null

// Nullable value
Optional<String> nullable = Optional.ofNullable(maybeNull);
// Returns Optional.empty() if argument is null

Retrieving Values

orElse — Provide a Default

String result = optional.orElse("default");
// Returns value if present, otherwise "default"

orElseGet — Lazy Default (Supplier)

String result = optional.orElseGet(() -> expensiveDefault());
// The supplier is only called if the Optional is empty

orElseThrow — Throw Exception on Empty

String result = optional.orElseThrow(() -> new IllegalArgumentException("Missing value"));

get — Direct (Avoid)

String result = optional.get();
// Throws NoSuchElementException if empty — use orElseThrow instead

Functional Operations

map

Optional<String> name = Optional.of("Alice");
Optional<Integer> length = name.map(String::length);
// Optional[5]

flatMap

Used when the mapping function itself returns an Optional:

public Optional<String> findNickname(String name) {
    Map<String, String> nicknames = Map.of("Alice", "Ali");
    return Optional.ofNullable(nicknames.get(name));
}

Optional<String> name = Optional.of("Alice");
Optional<String> nickname = name.flatMap(this::findNickname);
// Optional[Ali]

filter

Optional<String> name = Optional.of("Alice");
Optional<String> longName = name.filter(s -> s.length() > 3);
// Optional[Alice]

Optional<String> shortName = name.filter(s -> s.length() > 10);
// Optional.empty()

ifPresent

optional.ifPresent(value -> System.out.println("Value: " + value));

ifPresentOrElse (Java 9+)

optional.ifPresentOrElse(
    value -> System.out.println("Value: " + value),
    () -> System.out.println("No value present")
);

or (Java 9+)

Optional<String> result = optional.or(() -> Optional.of("fallback"));

Stream-like Chaining

Optional supports fluent chaining with map, flatMap, and filter:

public class UserService {
    public Optional<String> getCityDisplayName(Long userId) {
        return findUser(userId)
            .flatMap(this::getAddress)
            .map(Address::getCity)
            .map(City::getDisplayName);
    }
}

Each step automatically propagates emptiness — if any step returns Optional.empty(), the final result is empty without nested null checks.

Optional with Streams

List<Optional<String>> optionals = List.of(
    Optional.of("a"),
    Optional.empty(),
    Optional.of("b")
);

// Java 9+: flatMap(Optional::stream)
List<String> values = optionals.stream()
    .flatMap(Optional::stream)
    .toList();
// [a, b]

// Pre-Java 9 alternative
List<String> values2 = optionals.stream()
    .filter(Optional::isPresent)
    .map(Optional::get)
    .toList();

Primitive Optional

OptionalInt optionalInt = OptionalInt.of(42);
OptionalLong optionalLong = OptionalLong.of(100L);
OptionalDouble optionalDouble = OptionalDouble.of(3.14);

Common Mistakes

  1. Using Optional.get() without checking. Always use orElse, orElseGet, or orElseThrow instead.
  2. Using Optional as a method parameter. public void Process(Optional<String> input) — this forces the caller to wrap in Optional. Accept the raw type and handle null at the boundary.
  3. Using Optional for fields. Optional is not Serializable and adds overhead. Use null checks or a default sentinel value for fields.
  4. Calling isPresent() then get() in separate steps. This is the Optional equivalent of a null check — use ifPresent, orElse, or map instead.
  5. Returning null from an Optional-returning method. If a method returns Optional, it should never return null. Return Optional.empty() to indicate absence.

Practice Questions

1. What is the difference between Optional.of() and Optional.ofNullable()?
of() requires non-null argument (throws NPE if null). ofNullable() accepts null and returns Optional.empty().

2. What is the difference between orElse and orElseGet?
orElse takes a value (always evaluated). orElseGet takes a Supplier (only called if empty). Use orElseGet when the default is expensive to compute.

3. How does flatMap differ from map on Optional?
map wraps the result in Optional. flatMap expects the mapping function to return an Optional (avoiding nested Optional<Optional<T>>).

4. Why should you not use Optional as a method parameter?
It forces the caller to construct an Optional, adds overhead, and is an anti-pattern. Use method overloading or @Nullable annotations instead.

5. What is the Java 9 or() method?
It returns the Optional if present, otherwise returns the Optional produced by the supplier (similar to orElseGet but returns Optional).

Challenge Question:
Write a UserLookupService with methods Optional<User> findById(Long id), Optional<Email> getEmail(User user), and Optional<String> getDomain(Email email). Chain these to safely get a user's email domain. Then write a method that collects all valid email domains from a list of user IDs, skipping IDs that fail at any step.

FAQ

Is Optional a monad?

Yes, Optional satisfies the monad laws: it has a unit (of), a bind (flatMap), and obeys associativity and identity laws. However, Java does not have first-class monad support.

Can Optional contain null?

No. Optional is designed to wrap non-null values. The ofNullable method converts null to Optional.empty(). If you need a container that can hold null, use a different approach.

Is Optional serializable?

No. Optional does not implement Serializable. If you need serializable optional-like behavior, use a nullable field with a default sentinel value.

What is the difference between Optional and `@Nullable`?

Optional makes nullability explicit in the type system at the cost of an object wrapper. @Nullable is a compile-time hint with no runtime overhead. Use Optional for return types; use @Nullable for parameters and fields.

Can I use Optional with Stream flatMap?

Yes. Stream.flatMap(Optional::stream) (Java 9+) converts a Stream<Optional<T>> to Stream<T>, filtering out empty optionals.

Mini Project

Write a program OptionalDemo.java that:

  1. Creates a UserRepository with findById(long id) returning Optional<User>
  2. Creates a UserService with methods that use map, flatMap, orElse, and orElseThrow
  3. Demonstrates orElse vs orElseGet with a logging side effect
  4. Uses ifPresentOrElse to handle both present and absent cases
  5. Processes a list of IDs, collecting only those where the user exists and is active (filter)
  6. Implements a Config class that uses Optional<String> for optional configuration values
  7. Shows the OptionalInt and OptionalDouble primitive variants
  8. Demonstrates the anti-pattern of Optional parameters and suggests better alternatives

What's Next

Optional handles synchronous absence of values, but modern applications need asynchronous operations. Lesson 37 covers CompletableFuture — asynchronous programming with supplyAsync, thenCompose, allOf, and Exception Handling.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro