← Back to Java series
πŸ”§
Modern Java
Modern Java Β· Prerequisite: previous lecture

19. Optional

`Optional<T>` is a container that **encodes "maybe a value, maybe none" in the type**. Using it as a return type for methods that might return null forces callers to handle the empty case and reduces NPEs.

JavamodernfunctionalOptional
Duration
⏱ ~1.5-2 hours
Level
πŸ“Š Intermediate-Advanced
Prerequisite
🎯 Previous lecture or equivalent knowledge
OUTCOME
`Optional<T>` is a container that **encodes "maybe a value, maybe none" in the type**. Using it as a return type for methods that might return null forces callers to handle the empty case and reduces NPEs.

What you'll learn

  • 1Use `Optional.of` / `ofNullable` / `empty`
  • 2Use `map` / `filter` / `orElse` / `orElseThrow` / `ifPresent`
  • 3Use `Optional` as a **return type** pattern
  • 4Avoid the anti-patterns (fields, parameters, collection elements)

Overview

`Optional<T>` is a container that **encodes "maybe a value, maybe none" in the type**. Using it as a return type for methods that might return null forces callers to handle the empty case and reduces NPEs.

Core Concepts

1) Creating

java
Optional<String> a = Optional.of("hi");          // NPE if null
Optional<String> b = Optional.ofNullable(maybe);  // accepts null
Optional<String> c = Optional.empty();

2) Extracting the value

java
String v = a.orElse("default");
String w = a.orElseThrow(() -> new IllegalStateException("missing"));
a.ifPresent(System.out::println);

Avoid `get()` β€” it throws when the value is absent.

3) Transform and filter

java
Optional<Integer> len = a.map(String::length);
Optional<String> nonEmpty = a.filter(s -> !s.isEmpty());

4) Anti-patterns

  • Do **not** use `Optional` for fields, parameters, or collection elements
  • Use only as a return type

5) Primitives

Use `OptionalInt` / `OptionalLong` / `OptionalDouble` for primitives to avoid boxing.

Examples

Example 1 β€” `Basic.java`

java
import java.util.Optional;

public class Basic {
    public static void main(String[] args) {
        Optional<String> a = Optional.of("hi");
        Optional<String> b = Optional.empty();
        System.out.println(a.orElse("default"));
        System.out.println(b.orElse("default"));
        a.ifPresent(s -> System.out.println("got " + s));
        b.ifPresent(s -> System.out.println("never"));
    }
}

**Output**

text
hi
default
got hi

Example 2 β€” `FindUser.java`

java
import java.util.Map;
import java.util.Optional;

public class FindUser {
    static final Map<Integer, String> DB = Map.of(1, "Jisoo", 2, "Minsu");

    static Optional<String> find(int id) {
        return Optional.ofNullable(DB.get(id));
    }

    public static void main(String[] args) {
        System.out.println(find(1).orElse("?"));
        System.out.println(find(99).orElse("?"));
    }
}

**Output**

text
Jisoo
?

Example 3 β€” `MapChain.java`

java
import java.util.Optional;

public class MapChain {
    public static void main(String[] args) {
        Optional<String> name = Optional.of("  Jisoo  ");
        int length = name.map(String::trim)
                         .map(String::length)
                         .orElse(0);
        System.out.println("length=" + length);
    }
}

**Output**

text
length=5

Example 4 β€” `OptionalThrow.java`

java
import java.util.Optional;

public class OptionalThrow {
    public static void main(String[] args) {
        try {
            String s = Optional.<String>empty()
                .orElseThrow(() -> new IllegalStateException("missing"));
            System.out.println(s);
        } catch (IllegalStateException e) {
            System.out.println("error: " + e.getMessage());
        }
    }
}

**Output**

text
error: missing

Common Mistakes

  1. Using `Optional` for fields β€” costs memory and clutters serialization
  2. Calling `get()` without checking β†’ `NoSuchElementException`
  3. Wrapping a result that's never null in `Optional` β€” unnecessary noise
  4. Boxing primitives with `Optional<Integer>` instead of `OptionalInt`
  5. Chaining `Optional` so deep it's less readable than a `null` check

Summary

  • `Optional` shines as a return type β€” it forces callers to handle the empty case
  • Prefer `orElse` / `orElseThrow` over `get()`
  • Don't use `Optional` for fields or parameters

Practice

# Practice - 19. Optional

## Exercise 1 β€” `findById`

  • File: `Homework01.java`
  • Key concepts: `Optional.ofNullable`, `orElse`

Requirements

  • Lookup IDs 1, 2, 99 in a `Map`, print name or `unknown`.

Expected output

text
Jisoo
Minsu
unknown

## Exercise 2 β€” Chain transformations

  • File: `Homework02.java`
  • Key concepts: `map`, `filter`

Requirements

  • Take `Optional<String>` of `" hello "`, trim, uppercase, and print length or 0.

Expected output

text
length=5

## Solutions After trying it yourself, compare with [`answer/`](./answer/).

Solution code (homework/answer/)

answer/Homework01.java

java
import java.util.Map;
import java.util.Optional;

/** findById. */
public class Homework01 {
    static final Map<Integer, String> DB = Map.of(1, "Jisoo", 2, "Minsu");

    static Optional<String> findById(int id) {
        return Optional.ofNullable(DB.get(id));
    }

    public static void main(String[] args) {
        int[] ids = {1, 2, 99};
        for (int id : ids) System.out.println(findById(id).orElse("unknown"));
    }
}

answer/Homework02.java

java
import java.util.Optional;

/** Chain transformations. */
public class Homework02 {
    public static void main(String[] args) {
        int len = Optional.of("  hello  ")
            .map(String::trim)
            .map(String::toUpperCase)
            .map(String::length)
            .orElse(0);
        System.out.println("length=" + len);
    }
}

Try It Yourself

bash
cd 05_modern/19_optional/src
javac MapChain.java
java MapChain

Next Lecture

[20_Date_Time](../20_λ‚ μ§œ_μ‹œκ°„/) β€” handle dates and times with the modern `java.time` API.

Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub β†—