Optional.empty()

Return an empty Optional instead of null so callers handle the not-found case with the same methods they use for a value.

Signature
public static <T> Optional<T> empty()
  • Added in Java Java 8

Return value

Optional<T>An empty Optional, holding no value.

Errors

Does not throw.

Optional.empty() gives you an Optional with no value. It is what a lookup returns when nothing matched, and what ofNullable(null), map and filter produce when they have nothing to pass on. An empty Optional prints as Optional.empty.

Why not just return null

A method declared as Optional<Order> findById(String id) makes a promise: the result itself is never null. Callers can chain map, orElse or orElseThrow without a guard. Break that promise once by returning null and every caller gets a NullPointerException on the Optional, which is worse than the plain null you were trying to avoid. Inside an Optional-returning method, every "nothing" branch should be return Optional.empty();.

Optional<Order> findById(String id) {
    for (Order o : orders) {
        if (o.id().equals(id)) return Optional.of(o);
    }
    return Optional.empty();
}

Generics and type inference

empty() is generic, and Java infers T from where the result goes: a return statement, an assignment, or a ternary branch. When there is no target, as in var x = Optional.empty(), the type becomes Optional<Object> and later calls such as map(n -> n * 2) will not compile. Give it a type witness: Optional.<Integer>empty(), or declare the variable with its full type.

Comparing

Check emptiness with isEmpty() or isPresent(). equals also works, since any two empty Optionals are equal. Do not compare with ==: Optional is a value-based class, and the documentation warns against relying on identity, even though the current implementation happens to reuse one shared empty instance.

Back-end angle

An empty result is not an error by itself. The service returns Optional.empty(); the layer that knows the HTTP contract decides whether that means 404, a default, or an empty JSON field. Keeping that decision out of the repository makes the same lookup reusable for a GET handler and for a background job that should just skip the record.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Returning from a repository or cache lookup when no row or entry matches the key.
  • Signalling that an optional request parameter was absent or failed to parse, without throwing.
  • Representing a missing optional field when mapping a database row or JSON body to a domain object.

Frequently asked questions

Should I return null or Optional.empty() in Java?

If the method's return type is Optional, always return Optional.empty(). Returning null from an Optional method causes NullPointerException in callers who reasonably chain methods on the result.

How do I check if an Optional is empty?

Call isEmpty() (Java 11) or !isPresent(). optional.equals(Optional.empty()) also works, but avoid == because Optional is a value-based class and identity is not guaranteed.

Why does Optional.empty() give me Optional<Object>?

Type inference had no target type, often because of var or a method chain. Use a type witness such as Optional.<String>empty(), or assign it to a variable declared as Optional<String>.

More examples

Why returning null from an Optional method hurts

The caller trusts the Optional return type and chains orElse straight away. A null result breaks that trust and throws, so every empty path must return Optional.empty().

Example · java
Optional<String> badLookup(String key) {
    return null; // bug: should be Optional.empty()
}

Optional<String> goodLookup(String key) {
    return Optional.empty();
}

void main() {
    IO.println(goodLookup("theme").orElse("light"));
    try {
        IO.println(badLookup("theme").orElse("light"));
    } catch (NullPointerException e) {
        IO.println("badLookup returned null, not an Optional");
    }
}
// Output:
// light
// badLookup returned null, not an Optional

Giving empty() a type when inference cannot

With var there is no target type, so the witness Optional.<Integer>empty() is needed for n * 2 to compile. In the ternary, the declared type Optional<Integer> is enough for Java to infer T.

Example · java
void main() {
    var noLimit = Optional.<Integer>empty();
    IO.println(noLimit.map(n -> n * 2).orElse(-1));

    String raw = null;
    Optional<Integer> limit = raw == null ? Optional.empty() : Optional.of(Integer.parseInt(raw));
    IO.println(limit);
}
// Output:
// -1
// Optional.empty

Dropping empty results from a stream

Optional.stream() (Java 9) returns a stream of zero or one element, so flatMap quietly removes every empty result. This is a tidy way to keep only the ids in a batch request that parsed.

Example · java
Optional<Integer> parse(String s) {
    try {
        return Optional.of(Integer.parseInt(s));
    } catch (NumberFormatException e) {
        return Optional.empty();
    }
}

void main() {
    List<String> ids = List.of("10", "x", "30", "", "50");
    List<Integer> valid = ids.stream()
            .map(s -> parse(s))
            .flatMap(Optional::stream)
            .toList();
    IO.println(valid);
}
// Output:
// [10, 30, 50]

Discussion

  • Be the first to comment on this lesson.