Skip to content

Java Optionals Tutorial

Java Optional is a container object that may or may not contain a value. It is primarily used to avoid NullPointerException and to express explicitly that a value can be absent.

Package: java.util.Optional


1. Creating Optionals

You can create an Optional in three main ways:

java
import java.util.Optional;

public class OptionalCreation {
    public static void main(String[] args) {
        // 1. Empty Optional
        Optional<String> emptyOpt = Optional.empty();

        // 2. Optional with non-null value
        Optional<String> valueOpt = Optional.of("Hello");

        // 3. Optional with nullable value
        String nullable = null;
        Optional<String> nullableOpt = Optional.ofNullable(nullable);
        
        System.out.println(emptyOpt);      // Optional.empty
        System.out.println(valueOpt);      // Optional[Hello]
        System.out.println(nullableOpt);   // Optional.empty
    }
}

Notes:

  • Optional.of() → Throws NullPointerException if value is null.
  • Optional.ofNullable() → Returns empty Optional if value is null.

2. Checking Presence

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

if (opt.isPresent()) {
    System.out.println("Value exists: " + opt.get());
}

// Java 11+ way
opt.ifPresent(value -> System.out.println("Value exists: " + value));
  • isPresent() → returns true if value exists
  • ifPresent(Consumer) → executes lambda if value exists

3. Retrieving Values Safely

java
Optional<String> opt = Optional.ofNullable(null);

// Provide default if empty
String value1 = opt.orElse("Default");
System.out.println(value1); // Default

// Lazy default (only computed if needed)
String value2 = opt.orElseGet(() -> "Lazy Default");
System.out.println(value2); // Lazy Default

// Throw exception if empty
String value3 = opt.orElseThrow(() -> new IllegalArgumentException("No value found"));

Key methods:

  • orElse(T other) → returns other if empty
  • orElseGet(Supplier<? extends T> supplier) → lazy computation of default
  • orElseThrow(Supplier<? extends X> exceptionSupplier) → throws if empty

4. Transforming Optionals

You can transform the content inside an Optional:

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

Optional<String> upperOpt = opt.map(String::toUpperCase);
System.out.println(upperOpt.get()); // JAVA

// FlatMap example (avoids nested Optionals)
Optional<String> nestedOpt = Optional.of("  hello  ");
Optional<String> trimmedOpt = nestedOpt.flatMap(s -> Optional.of(s.trim()));
System.out.println(trimmedOpt.get()); // hello
  • map(Function) → transforms value if present
  • flatMap(Function) → transforms and flattens nested Optionals

5. Filtering Optionals

You can filter the value:

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

Optional<String> filtered = opt.filter(s -> s.startsWith("J"));
System.out.println(filtered.isPresent()); // true

Optional<String> filtered2 = opt.filter(s -> s.startsWith("K"));
System.out.println(filtered2.isPresent()); // false
  • filter(Predicate) → returns empty Optional if predicate fails

6. Optional with Streams

Optionals integrate well with Streams:

java
List<String> list = List.of("apple", "banana", "cherry");

Optional<String> first = list.stream()
                             .filter(s -> s.startsWith("b"))
                             .findFirst();

first.ifPresent(System.out::println); // banana
  • findFirst(), findAny() → return Optional
  • Use Optionals to avoid null checks when processing streams

7. Best Practices

  1. Don’t use Optional in fields or collections

    • Use Optional for method return types, not class members.
  2. Avoid get() without checks

    • Prefer orElse, orElseGet, or ifPresent.
  3. Chaining transformations

    java
    String result = Optional.ofNullable("  java  ")
                            .map(String::trim)
                            .filter(s -> !s.isEmpty())
                            .map(String::toUpperCase)
                            .orElse("DEFAULT");
    System.out.println(result); // JAVA
  4. Use Optional in APIs to express optional values

    java
    Optional<User> findUserById(String id);

8. Advanced Examples

8.1 Combining Optionals

java
Optional<String> a = Optional.of("Hello");
Optional<String> b = Optional.of("World");

String combined = a.flatMap(av -> b.map(bv -> av + " " + bv))
                   .orElse("Default");
System.out.println(combined); // Hello World

8.2 Avoiding Nested Null Checks

java
class Person {
    private Address address;
    public Address getAddress() { return address; }
}

class Address {
    private String city;
    public String getCity() { return city; }
}

Person person = new Person();
String city = Optional.ofNullable(person)
                      .map(Person::getAddress)
                      .map(Address::getCity)
                      .orElse("Unknown");
System.out.println(city); // Unknown

9. Summary of Common Methods

MethodDescription
of(T value)Create Optional with non-null value
ofNullable(T value)Create Optional that may be null
empty()Empty Optional
isPresent()Check if value exists
ifPresent(Consumer)Execute lambda if present
orElse(T other)Default value if empty
orElseGet(Supplier)Lazy default if empty
orElseThrow(Supplier)Throw exception if empty
map(Function)Transform value
flatMap(Function)Transform and flatten nested Optionals
filter(Predicate)Keep value if predicate true

✅ Key Takeaways

  • Optionals help eliminate null and make APIs more readable.
  • They are best used for return types, not fields.
  • Chain map, flatMap, filter, and orElse to handle values safely.
  • Integrates nicely with Java Streams and functional patterns.

© 2023-2025 Maduranga Kannangara. Feel free to use or share this content. Attribution is appreciated but not required.