A container that holds either one value or nothing — a return type that forces callers to think about absence instead of forgetting a null check.
Creating
| Factory | When |
|---|---|
Optional.of(v) | v is definitely non-null — throws NullPointerException otherwise |
Optional.ofNullable(v) | v may be null → empty |
Optional.empty() | explicitly no value |
Using the value — don't just call get()
import java.util.*;
public class Main {
static Map<String, String> emails = Map.of("asha", "asha@mail.com");
static Optional<String> findEmail(String user) {
return Optional.ofNullable(emails.get(user));
}
public static void main(String[] args) {
System.out.println(findEmail("asha"));
System.out.println(findEmail("ravi"));
System.out.println(findEmail("ravi").orElse("no-email"));
System.out.println(findEmail("asha").map(String::toUpperCase).orElse("?"));
System.out.println(findEmail("asha").filter(e -> e.endsWith(".org")).isPresent());
findEmail("asha").ifPresent(e -> System.out.println("send to " + e));
findEmail("ravi").ifPresentOrElse(
e -> System.out.println("send to " + e),
() -> System.out.println("nobody to email"));
try {
findEmail("ravi").orElseThrow();
} catch (NoSuchElementException e) {
System.out.println(e.getMessage());
}
}
}Optional[asha@mail.com]
Optional.empty
no-email
ASHA@MAIL.COM
false
send to asha@mail.com
nobody to email
No value present| Method | Does |
|---|---|
isPresent() / isEmpty() (11+) | test |
get() / orElseThrow() (10+) | value or NoSuchElementException — prefer the clearer orElseThrow() |
orElse(x) | value or x (x is always evaluated) |
orElseGet(supplier) | value or supplier.get() (evaluated only if empty) |
orElseThrow(() -> new MyEx()) | value or your exception |
map(f) / flatMap(f) | transform if present (flatMap when f already returns an Optional) |
filter(pred) | empty unless the predicate holds |
ifPresent(c) / ifPresentOrElse(c, r) (9+) | run code |
or(supplier) (9+) | fall back to another Optional |
stream() (9+) | 0- or 1-element stream |
The orElse vs orElseGet trap
import java.util.*;
public class Main {
static String expensiveDefault() {
System.out.println(" (computing default...)");
return "default";
}
public static void main(String[] args) {
Optional<String> name = Optional.of("Asha");
System.out.println("orElse:");
System.out.println(name.orElse(expensiveDefault())); // default is computed anyway!
System.out.println("orElseGet:");
System.out.println(name.orElseGet(Main::expensiveDefault)); // lazy — not called
}
}orElse:
(computing default...)
Asha
orElseGet:
Ashamap vs flatMap — chaining without null checks
import java.util.*;
public class Main {
record Address(String city) {}
record User(String name, Optional<Address> address) {}
public static void main(String[] args) {
User withAddr = new User("Asha", Optional.of(new Address("Pune")));
User without = new User("Ravi", Optional.empty());
for (User u : List.of(withAddr, without)) {
String city = Optional.of(u)
.flatMap(User::address) // already Optional → flatMap
.map(Address::city) // plain value → map
.orElse("unknown");
System.out.println(u.name() + ": " + city);
}
}
}Asha: Pune
Ravi: unknownHow not to use Optional
Optional is not Serializable, adds an allocation, and a parameter of type Optional can itself be null — so it solves nothing there. Primitive versions OptionalInt, OptionalLong, OptionalDouble avoid boxing (returned by IntStream.max() etc.).
Why was Optional added?
Difference between orElse and orElseGet?
Should you use Optional as a field or method parameter?