Optional makes "might be missing" visible in method signatures and cuts down on NullPointerExceptions.
Optional<T> is a container that either holds a value or is empty. Its main job is as a return type for methods that may legitimately find nothing, such as findUserByEmail. The signature tells callers they must deal with absence, which a nullable return never does.
Create one with Optional.of(value) (value must be non-null), Optional.ofNullable(maybeNull), or Optional.empty(). Streams return them from findFirst, max, min and reduce.
Consume it without unwrapping by hand:
map(f)/flatMap(f)— transform the value if present.filter(pred)— keep the value only if it matches.orElse(default)— fallback value (always evaluated).orElseGet(() -> compute())— fallback computed lazily, only when empty.orElseThrow()— get the value or throwNoSuchElementException;orElseThrow(() -> new NotFound(id))for your own exception.ifPresent(consumer)andifPresentOrElse(consumer, runnable).
Calling get() after checking isPresent() works but is just null-checking with extra steps; the methods above are more expressive.
Guidelines the JDK designers recommend:
- Use Optional for return values, not for fields, method parameters or collection elements.
- Never return
nullfrom a method declared to returnOptional. - Return an empty collection rather than
Optional<List<T>>. - For primitives there are
OptionalInt,OptionalLongandOptionalDouble.
Prefer orElseGet over orElse when the fallback is expensive, such as a database call — orElse's argument is evaluated even when the value is present.
Example
import java.util.Map;
import java.util.Optional;
public class Optionals {
record User(String email, String nickname) { }
static final Map<String, User> USERS = Map.of(
"ana@x.io", new User("ana@x.io", "ana"),
"bo@x.io", new User("bo@x.io", null));
static Optional<User> findByEmail(String email) {
return Optional.ofNullable(USERS.get(email));
}
public static void main(String[] args) {
for (String email : new String[]{"ana@x.io", "bo@x.io", "zed@x.io"}) {
String nick = findByEmail(email).map(User::nickname).orElse("anonymous");
System.out.print(nick + " "); // ana anonymous anonymous
}
findByEmail("ana@x.io").ifPresent(u -> System.out.println("\nfound " + u.email()));
User u = findByEmail("zed@x.io")
.orElseThrow(() -> new IllegalStateException("no such user")); // throws
}
}Remember
- Use Optional as a return type for "may be absent" results
- Chain
map/filterand finish withorElse,orElseGetororElseThrow - Don't use Optional for fields or parameters, and never return null instead of one
Watch out for
- Writing
if (opt.isPresent()) return opt.get();everywhere — it's a null check in disguise.