> For AI agents: the complete documentation index is available at https://nhanbe.id.vn/llms.txt, the full documentation bundle is available at https://nhanbe.id.vn/llms-full.txt.

# Optionals: Avoiding NullPointerException

`Optional<T>` is a container that may or may not hold a value. It forces callers to
explicitly handle the "no value" case instead of silently passing `null` around until
something throws a `NullPointerException`.

## Quick start

```java
// Returns Optional instead of nullable String
Optional<String> findUserName(int id) {
    User user = database.findById(id);
    return Optional.ofNullable(user).map(User::name);
}

// Caller is forced to handle the empty case
String name = findUserName(42).orElse("Unknown");
```

***

## Creating Optionals

```java
// Non-null value (throws NullPointerException if null!)
Optional<String> present = Optional.of("hello");

// Nullable value (empty if null)
Optional<String> maybe = Optional.ofNullable(getNullableValue());

// Empty optional
Optional<String> empty = Optional.empty();
```

> Use `Optional.of()` when you are certain the value is non-null. Use
> `Optional.ofNullable()` when the value might be null.

***

## Unwrapping values

### Safe unwrapping

```java
Optional<String> opt = Optional.of("hello");

// Default value
String val1 = opt.orElse("default");

// Lazy default (only computed if empty)
String val2 = opt.orElseGet(() -> expensiveComputation());

// Throw if empty
String val3 = opt.orElseThrow();  // NoSuchElementException
String val4 = opt.orElseThrow(() -> new NotFoundException("User not found"));

// Conditional execution
opt.ifPresent(System.out::println);

// Handle both cases (Java 9+)
opt.ifPresentOrElse(
    value -> System.out.println("Found: " + value),
    ()    -> System.out.println("Not found")
);
```

### Avoid: `get()` without checking

```java
// BAD - throws NoSuchElementException if empty
String value = opt.get();

// If you must use get(), at least check first
if (opt.isPresent()) {
    String value = opt.get();
}

// But this defeats the purpose. Use orElse/orElseThrow instead.
```

***

## Chaining operations

### map

Transform the value if present:

```java
Optional<String> name = Optional.of("  Alice  ");

Optional<String> cleaned = name
    .map(String::strip)
    .map(String::toUpperCase);
// Optional[ALICE]

// If empty, map is skipped
Optional<String> empty = Optional.<String>empty()
    .map(String::toUpperCase);
// Optional.empty
```

### flatMap

Use when the transformation itself returns an Optional (avoids `Optional<Optional<T>>`):

```java
Optional<User> findUser(int id) { ... }
Optional<Address> getAddress(User user) { ... }

// map would give Optional<Optional<Address>> - wrong!
// flatMap unwraps the inner Optional
Optional<String> city = findUser(42)
    .flatMap(this::getAddress)
    .map(Address::city);
```

### filter

Keep the value only if it matches a predicate:

```java
Optional<Integer> age = Optional.of(25);

Optional<Integer> adult = age.filter(a -> a >= 18);
// Optional[25]

Optional<Integer> senior = age.filter(a -> a >= 65);
// Optional.empty
```

### or (Java 9+)

Provide an alternative Optional if empty:

```java
Optional<String> primary = findInCache(key);
Optional<String> fallback = primary.or(() -> findInDatabase(key));
```

### stream (Java 9+)

Convert to a Stream (0 or 1 elements), useful in stream pipelines:

```java
List<Optional<String>> optionals = List.of(
    Optional.of("a"),
    Optional.empty(),
    Optional.of("b"),
    Optional.empty(),
    Optional.of("c")
);

List<String> values = optionals.stream()
    .flatMap(Optional::stream)
    .toList();
// [a, b, c]
```

***

## Chaining example: user profile lookup

```java
record User(String name, Optional<Address> address) {}
record Address(String street, String city, Optional<String> zipCode) {}

String getZipCode(Optional<User> user) {
    return user
        .flatMap(User::address)
        .flatMap(Address::zipCode)
        .map(String::strip)
        .filter(zip -> !zip.isEmpty())
        .orElse("N/A");
}
```

***

## Optional with streams

```java
// findFirst and findAny return Optional
Optional<String> firstLong = List.of("hi", "hello", "hey")
    .stream()
    .filter(s -> s.length() > 3)
    .findFirst();
// Optional[hello]

// min and max return Optional
Optional<Integer> max = List.of(3, 7, 2, 9)
    .stream()
    .max(Integer::compareTo);
// Optional[9]

// reduce without identity returns Optional
Optional<Integer> sum = List.of(1, 2, 3)
    .stream()
    .reduce(Integer::sum);
// Optional[6]
```

***

## Optional vs null: when to use which

| Use Optional                                            | Use null                                           |
| ------------------------------------------------------- | -------------------------------------------------- |
| Method return types where "no value" is a valid outcome | Private fields (Optional has memory overhead)      |
| API boundaries (public methods)                         | Performance-critical inner loops                   |
| Stream terminal operations (findFirst, min, max)        | When the method contract clearly states non-null   |
| When you want to chain transformations                  | Collections (use empty collection instead of null) |

### Rule of thumb

- **Return** `Optional` from methods that might not have a result
- **Accept** `null` (not Optional) in method parameters - use `@Nullable` annotation
- **Never** store Optional as a field (it is not `Serializable` and adds overhead)
- **Never** use Optional in collections (`List<Optional<String>>` is a code smell)

***

## Anti-patterns

### Using Optional as a field

```java
// BAD
class User {
    private Optional<String> middleName; // overhead, not serializable
}

// GOOD
class User {
    private String middleName; // nullable

    public Optional<String> getMiddleName() {
        return Optional.ofNullable(middleName);
    }
}
```

### Optional as method parameter

```java
// BAD - forces callers to wrap values
void process(Optional<String> name) { ... }
process(Optional.of("Alice")); // clunky

// GOOD - accept nullable, overload, or use a default
void process(String name) { ... }
void process() { process(null); }
```

### Wrapping and immediately unwrapping

```java
// BAD - pointless
String value = Optional.ofNullable(getValue()).orElse("default");

// GOOD - use a simple null check
String value = getValue();
if (value == null) value = "default";

// UNLESS you need to chain operations
String value = Optional.ofNullable(getValue())
    .map(String::strip)
    .filter(s -> !s.isEmpty())
    .orElse("default");
// This is fine - the chain justifies the Optional
```

### Using `isPresent()` + `get()` instead of functional methods

```java
// BAD - imperative style defeats the purpose
if (optional.isPresent()) {
    return optional.get().toUpperCase();
} else {
    return "DEFAULT";
}

// GOOD
return optional.map(String::toUpperCase).orElse("DEFAULT");
```

***

## Common pitfalls

| Pitfall                           | Problem                                                 | Fix                                                                |
| --------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------ |
| `Optional.of(null)`               | Throws `NullPointerException` immediately               | Use `Optional.ofNullable(value)`                                   |
| `Optional.get()` without check    | Throws `NoSuchElementException`                         | Use `orElse()`, `orElseThrow()`, or `ifPresent()`                  |
| Optional as field                 | Not Serializable; memory overhead                       | Store null, expose `Optional` via getter                           |
| `orElse()` with expensive default | `orElse(expensiveCall())` always evaluates the argument | Use `orElseGet(() -> expensiveCall())` for lazy evaluation         |
| Optional in collections           | `List<Optional<String>>` is confusing                   | Filter nulls before collecting, or use `flatMap(Optional::stream)` |
| Nested Optionals                  | `Optional<Optional<T>>` from `map` returning Optional   | Use `flatMap` instead of `map`                                     |

***
