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.
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
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().
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 OptionalGiving 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.
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.emptyDropping 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.
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