Course contents

A guide to Java Optional

Why Optional exists, creating and unwrapping it safely, the orElse versus orElseGet trap, map, flatMap, filter and the Java 9 methods, the field and parameter anti-patterns, primitive optionals and what it costs.

PremiumOften askedLesson17 min readCoreReviewed Oct 2026 · Java 21
On this page
  1. Picture it first: a gift box that might be empty
  2. Creating and unwrapping, under the hood
  3. The functional methods: map, flatMap, filter and the Java 9 additions
  4. Real world: where Optional belongs and where it hurts
  5. Advanced: primitive optionals, cost and null checks
  6. How to say it in the interview

🎁 What is Optional?

Optional<T> is a small container that holds either one non-null value or nothing. A method like Optional<User> findByEmail(String email) tells the caller, in its signature, "there may be no user, and you have to decide what to do about it". Before Java 8 the same method returned a User that was sometimes null, and the only warning was a comment nobody read.

Optional is one of those APIs that everyone uses and few people use well. Interviewers ask about it because the mistakes are so recognisable: opt.get() without a check, orElse(expensiveCall()), Optional fields on entities, Optional<List<Order>>. A good answer shows you know what it was designed for and, just as important, where it doesn't belong.

What you’ll learn

  • Why Optional was designed as a return type, and when of, ofNullable and empty fit
  • Why orElseThrow() beats get(), and why orElse is eager while orElseGet is lazy
  • How to replace nested null checks with map, flatMap, filter, or, ifPresentOrElse and stream()
  • Why Optional doesn't belong in fields, parameters or collections, and why it isn't Serializable
  • When to use OptionalInt and friends, and what an Optional really costs with escape analysis

For this lesson, we'll focus on why Optional exists, how to create one, how to get the value out safely, the orElse versus orElseGet trap, the functional methods (map, flatMap, filter, or, stream and friends), the anti-patterns, primitive optionals, what an Optional costs, and how it compares with a plain null check.

Below the line (out of scope)

The 30-second answer

Optional arrived in Java 8 as a return type for methods that may have no result, so absence is visible in the signature instead of hidden in a null. You create one with Optional.of (non-null, throws otherwise), ofNullable or empty. You get the value out with orElse, orElseGet, orElseThrow or by transforming it with map, flatMap and filter, not with a bare get(). orElse evaluates its argument eagerly, so use orElseGet for anything expensive. Don't use Optional for fields, parameters or collections, and remember it isn't Serializable. It costs a 16-byte allocation that escape analysis often removes, so it's fine on most paths but not in the hottest loops.

Interview radar

Asked very often: What's the difference between orElse and orElseGet, and why shouldn't you call get() without checking? Senior rounds probe: When to use map versus flatMap, why Optional shouldn't be a field or parameter, and what it costs compared with a null check. Production angle: An orElse(loadFromDatabase()) that quietly runs a database query on every cache hit.

01Picture it first: a gift box that might be empty

Think of Optional as a gift box with a label that says "may be empty". A raw reference is a box with no label: you reach in confidently and sometimes your hand closes on nothing (a NullPointerException). The labelled box forces a moment of thought. You can open it and use what's inside, pass it along with a transformation ("if there's a shirt, wrap it in paper"), or decide upfront what to do if it's empty ("if empty, hand over a gift card").

The important part of the analogy: you never pass an empty box as a gift. Optional is for the moment of handing back a result, not for storing things in your house or asking guests to bring boxes.

Here's how a chain of Optional operations behaves, depending on whether the box has something in it:

Fig. 1

Two lanes: on the top lane a present value flows through map, filter and then orElse returns the transformed value; on the bottom lane an empty Optional skips map and filter and orElse returns the default, and a filter that fails moves a present value down to the empty lane

Two lanes: on the top lane a present value flows through map, filter and then orElse returns the transformed value; on the bottom lane an empty Optional skips map and filter and orElse returns the default, and a filter that fails moves a present value down to the empty lane

02Creating and unwrapping, under the hood

There are three factory methods, and picking the right one is the first thing interviewers notice.

Try it yourself first

Before reading on: which of these lines throws, and what does each println show?

Optional<String> a = Optional.of("ORD-42");
Optional<String> b = Optional.ofNullable(null);
Optional<String> c = Optional.empty();
Optional<String> d = Optional.of(null);

System.out.println(a);              // ?
System.out.println(b.equals(c));    // ?
Exception in thread "main" java.lang.NullPointerException

The program dies on line 4, because Optional.of rejects null with a NullPointerException. Remove that line and you get Optional[ORD-42] and true.

Notice three things:

  • Use of when a null would be a bug. The NPE happens right there, at the point of creation, rather than three methods later.
  • Use ofNullable at the boundary with code that returns null, such as Map.get or a legacy DAO.
  • empty() returns a shared singleton, so it costs nothing. Optional overrides equals to compare contents, which is why b.equals(c) is true.

Inside the JDK, Optional is a final class with a single field, private final T value, where null means empty. That's the whole implementation: every method is an if (value == null) check wrapped in a nice API.

get() versus orElseThrow()

opt.get() returns the value or throws NoSuchElementException("No value present"). The problem isn't what it does, it's that the name sounds harmless, so people call it without checking. Java 10 added the no-argument orElseThrow(), which does exactly the same thing with a name that admits it can fail. The javadoc now recommends it over get().

User u1 = userRepository.findById(id).get();                            // works, hides the risk
User u2 = userRepository.findById(id).orElseThrow();                     // same, honest name
User u3 = userRepository.findById(id)
        .orElseThrow(() -> new UserNotFoundException(id));               // best: a meaningful error

The third form is what I write in service code. A UserNotFoundException can map to a 404, while a NoSuchElementException from deep inside a stack trace becomes a 500 and a confused on-call engineer.

orElse versus orElseGet: the eager-evaluation trap

This is the most common Optional interview question, and also the one that causes real production bugs.

Try it yourself first

Before reading on: the user is in the cache. How many times does loadFromDatabase run?

User loadFromDatabase(long id) {
    System.out.println("DB call for " + id);
    return userDao.find(id);
}

Optional<User> cached = cache.get(42L);                    // present
User u1 = cached.orElse(loadFromDatabase(42L));             // prints: DB call for 42
User u2 = cached.orElseGet(() -> loadFromDatabase(42L));    // prints nothing

orElse takes a value, and Java evaluates method arguments before the call. So loadFromDatabase(42L) runs every time, even when the cache hit makes its result useless. orElseGet takes a Supplier, and only calls it when the Optional is empty.

Fig. 2

Timeline comparison: with orElse the database call runs before orElse is entered and its result is thrown away when a value is present; with orElseGet only a lambda is created and the database call happens only on the empty path

Timeline comparison: with orElse the database call runs before orElse is entered and its result is thrown away when a value is present; with orElseGet only a lambda is created and the database call happens only on the empty path

Premium lesson

Keep reading with the Java Pass

The rest of this lesson is part of Java One-Stop Interview Prep: in-depth explanations, diagrams, model answers and how to say them in the interview.

  • The functional methods: map, flatMap, filter and the Java 9 additions
  • Real world: where Optional belongs and where it hurts
  • Advanced: primitive optionals, cost and null checks
  • How to say it in the interview

From ₹199 · one-time payment · No auto-renewal

Practise this topicRecords, enums & Optional: interview questions & scenarios
Further reading (6)