java-optional-nullpointerexception-evitar-null

Optional in Java: Representing Absent Values

  • 4 min

An Optional is a container that may or may not hold a value and makes that possibility explicit in the API.

Tony Hoare, the inventor of the null reference, called his own creation “the billion-dollar mistake”.

NullPointerException occurs when an operation needs an object, but the reference contains null. It’s a frequent failure, although there is no universal percentage describing all Java projects.

To avoid it, our code used to be filled with “defenses”:

// The hell of nested IFs
public String getCityName(User u) {
    if (u != null) {
        Address a = u.getAddress();
        if (a != null) {
            return a.getCity();
        }
    }
    return "Unknown";
}
Copied!

For this we have java.util.Optional<T> to explicitly represent that a method might not return a value. It doesn’t eliminate null from the language, but it improves certain return APIs.

What is an Optional?

Think of Optional as a Box. When a method returns Optional<String>, it’s handing you a sealed box.

  • There might be a String inside (the value).
  • The box might be full of air (empty).

The important thing is that you (the programmer) are forced to open the box carefully. You can no longer ignore the possibility that it might be empty.

How to create an Optional

You will rarely create Optionals manually (you usually receive them from libraries or Streams), but you should know how to put things in the box.

// 1. EMPTY box
Optional<String> empty = Optional.empty();

// 2. Box with something NON-NULL (If you put null, it explodes right here)
Optional<String> full = Optional.of("Hello");

// 3. SAFE box (If it's null, creates an empty box; if not, puts the value)
// THIS IS THE ONE YOU'LL USE MOST
String possiblyNullText = getTextFromDB();
Optional<String> box = Optional.ofNullable(possiblyNullText);
Copied!

How to retrieve the value (the safe way)

This is where Optional shines. It offers a fluent API to decide what to do if the box is empty.

Default value (orElse)

“If there’s something, give it to me. If not, give me this default value.”

String name = box.orElse("Anonymous User");
Copied!

Lazy default value (orElseGet)

“If there’s something, give it to me. If not, execute this code to calculate the default value.” This uses a Supplier and avoids calculating the alternative value if it’s not needed, which is important when the operation is costly.

// Only calls queryDB() if the box was empty
String name = box.orElseGet(() -> queryDB());
Copied!

Throw exception (orElseThrow)

“If there’s something, give it to me. If not, throw this error.”

// Very useful for mandatory validations
String name = box.orElseThrow(() -> new UserNotFoundException("Does not exist"));
Copied!

Transforming without opening the box (map)

This way you can transform the content without extracting it manually. If the box was empty, the result is still an empty box. If it was full, the transformation is applied.

Let’s go back to the initial example. See how clean it is:

public String getCityName(Optional<User> optUser) {
    return optUser
            .map(User::getAddress)  // If there's a user, get their address
            .map(Address::getCity)  // If there's an address, get its city
            .orElse("Unknown");     // If something failed along the way, return this.
}
Copied!

Not a single if! The Optional handles intermediate nulls automatically.

The anti-pattern: .get() and .isPresent()

When beginners start with Optional, they often do this:

// BAD ❌: This is the same as using != null but uglier
if (box.isPresent()) {
    System.out.println(box.get());
}
Copied!

The .get() method is dangerous. If the box is empty and you call .get(), Java throws NoSuchElementException (which is basically the new NullPointerException). Avoid using .get() without first checking for the presence of the value. Depending on the case, use orElse, orElseGet, orElseThrow, ifPresent, or map.

If you just want to do something if it exists (side effect):

// GOOD ✅: Functional style
box.ifPresent(value -> System.out.println(value));
Copied!

Optional and streams

Optional is the best friend of Streams. Many terminal operations return an Optional because they don’t know if the stream carried data.

// Find the first user who is from "Madrid"
Optional<User> possibleMadrilenian = users.stream()
    .filter(u -> "Madrid".equals(u.getCity()))
    .findFirst();

// Use the result safely
possibleMadrilenian.ifPresent(u -> sendEmail(u));
Copied!