Java Optional in Depth
Optional discipline is a set of habits for handling a missing value in the open. flatMap, orElseThrow and ifPresentOrElse give each missing-value case its own clear handling.
flatMap keeps the chain flat
A city lookup returns an Optional, and a country lookup takes a city and returns an Optional too. flatMap merges those two layers into one Optional of a country name.
import java.util.Optional;
public class Main {
static Optional<String> findCity(String name) {
if (name.equals("Ana")) {
return Optional.of("Lisbon");
}
return Optional.empty();
}
static Optional<String> findCountry(String city) {
if (city.equals("Lisbon")) {
return Optional.of("Portugal");
}
return Optional.empty();
}
public static void main(String[] args) {
Optional<String> country = findCity("Ana")
.flatMap(city -> findCountry(city));
System.out.println(country.orElse("unknown"));
}
}orElseThrow names the failure
orElseThrow raises an exception when the Optional is empty. The catch block prints the message the program chose for that failure.
import java.util.Optional;
public class Main {
static Optional<String> findCity(String name) {
if (name.equals("Ana")) {
return Optional.of("Lisbon");
}
return Optional.empty();
}
public static void main(String[] args) {
try {
String city = findCity("Ravi")
.orElseThrow(() -> new IllegalStateException("No city on file for Ravi"));
System.out.println(city);
} catch (IllegalStateException e) {
System.out.println(e.getMessage());
}
}
}ifPresentOrElse on the present path
ifPresentOrElse takes two actions and picks one of them. A present value runs the first action with that value.
import java.util.Optional;
public class Main {
static Optional<String> findCity(String name) {
if (name.equals("Ana")) {
return Optional.of("Lisbon");
}
return Optional.empty();
}
public static void main(String[] args) {
findCity("Ana").ifPresentOrElse(
city -> System.out.println("City: " + city),
() -> System.out.println("No city on file"));
}
}ifPresentOrElse on the empty path
The same call shape handles the empty case in the same place. An empty Optional runs the second action instead.
import java.util.Optional;
public class Main {
static Optional<String> findCity(String name) {
if (name.equals("Ana")) {
return Optional.of("Lisbon");
}
return Optional.empty();
}
public static void main(String[] args) {
findCity("Ravi").ifPresentOrElse(
city -> System.out.println("City: " + city),
() -> System.out.println("No city on file"));
}
}Example
import java.util.Optional;
public class Main {
static Optional<String> findCity(String name) {
if (name.equals("Ben")) {
return Optional.of("Porto");
}
return Optional.empty();
}
static Optional<String> findCountry(String city) {
if (city.equals("Porto")) {
return Optional.of("Portugal");
}
return Optional.empty();
}
public static void main(String[] args) {
Optional<String> country = findCity("Ben")
.flatMap(city -> findCountry(city));
System.out.println(country.orElse("unknown"));
try {
String city = findCity("Zara")
.orElseThrow(() -> new IllegalStateException("No city on file for Zara"));
System.out.println(city);
} catch (IllegalStateException e) {
System.out.println(e.getMessage());
}
findCity("Ben").ifPresentOrElse(
city -> System.out.println("City: " + city),
() -> System.out.println("No city on file"));
}
}Key points
- Use flatMap when the next step already returns an Optional, so the result stays one level deep.
- Never call get() on an unchecked Optional, because an empty Optional has no value to give.
- Optional works as a return type, not as a field or a parameter.
- orElseThrow turns a missing value into an exception with a message the program chooses.
- ifPresentOrElse runs one action for a present value and a different action for an empty one.
- Optional discipline handles the missing case in code, rather than hiding it behind a null check far away.
In this example, one program uses all three tools on the same pair of lookups. flatMap prints a country value, orElseThrow prints its own message for a missing city, and ifPresentOrElse prints the present path for a found city.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
A step in a lookup chain already returns an Optional. Which method keeps the result as one Optional instead of an Optional inside an Optional?