The Optional Type

An Optional<T> is a box (or wrapper) that can either hold a value of type T or be empty.

Optional is safer than using null because it makes you handle the “maybe empty” case in a clear way. But it is only safer if you use it correctly.

Getting a Value from Optional

The main idea of Optional is: don’t take the value directly, but instead use methods that give you a safe result when the value is missing.

There are a few strategies:

Give a default value

String result = optionalString.orElse("");
// Uses the value if present, otherwise ""

Compute a default only when needed

String result = optionalString.orElseGet(() -> System.getProperty("myapp.default"));
// Calls the function only if the Optional is empty

Throw an exception if empty

String result = optionalString.orElseThrow(IllegalStateException::new);
// Throws the exception when no value is present

Using a Value If It Exists

In the last section, we looked at how to give a default value when an Optional is empty. Another way is to use the value only if it exists;


Working with Optional in a Pipeline

So far, we looked at ways to get a value out of an Optional. Another good strategy is to keep the Optional and transform it step by step.


How Not to Use Optional

Many developers like the Optional type in Java because it makes code safer and easier to read. But if you use it in the wrong way, it is no better than the old “value or null” style.

Why get() Is a Problem

The method get() gives you the value inside an Optional if one is present. But if the Optional is empty, get() will throw an exception.

Optional<User> maybeUser = ...;
maybeUser.get().sendEmail();

This code is just as dangerous as:

User user = ...;
user.sendEmail();

In both cases, you risk an error if there is no user. Using get() directly makes your code no safer than working with null.

Why isPresent() and isEmpty() Don’t Help Much

You might try:

if (maybeUser.isPresent()) {
    maybeUser.get().sendEmail();
}

But this is almost the same as:

if (user != null) {
    user.sendEmail();
}

The logic is clearer, but you have not really solved the problem. You are still checking manually instead of using better features of Optional.

Better Approaches

Instead of calling get(), try these methods:

maybeUser.ifPresent(user -> user.sendEmail());

The code runs only if a user is present.

User user = maybeUser.orElse(new GuestUser());

You provide a default value when the Optional is empty.

User user = maybeUser.orElseThrow();

This makes it clear that the program should fail if no user exists. Only use this when you are sure the value is there.

Common Mistakes to Avoid

Here are some tips for writing cleaner code with Optional:


Creating Optional Values in Java

Until now, we have mostly looked at how to use an Optional object created by someone else. But sometimes you need to create your own Optional. Java provides several static methods to do this.

Combining Optional Values Using flatMap

Sometimes, we have a method that returns an Optional<T>, and the type T itself has a method that returns an Optional<U>. If these were normal methods, we might just write s.f().g(). But that doesn’t work here because s.f() gives an Optional<T>, not a T.

Instead, we can use flatMap to chain these calls safely:

Optional<U> result = s.f().flatMap(T::g);

Here’s what happens:

This technique can be repeated for multiple methods or functions that return Optional values. By chaining flatMap calls, you can create a pipeline of operations that only produces a result if all steps succeed.