Skip to content
intermediatePhase 15 · Java 8+ Features

Optional

Handle null values elegantly with Optional.

30m
1 problems
Topic Progress0%

Creating Optionals

Creating Optional Instances

Optional<T> is a container object that may or may not contain a non-null value. It provides methods to handle the presence or absence of a value gracefully.

Factory methods:

  • Optional.of(T value) — wraps a non-null value (throws NPE if null)
  • Optional.ofNullable(T value) — wraps a value that may be null
  • Optional.empty() — creates an empty Optional
import java.util.Optional;

public class CreatingOptionalsDemo {
    public static void main(String[] args) {
        // of() - value must not be null
        Optional<String> present = Optional.of("Hello");
        System.out.println("Present: " + present.isPresent()); // true
        System.out.println("Value: " + present.get()); // Hello

        // of() with null throws NullPointerException
        try {
            Optional<String> bad = Optional.of(null);
        } catch (NullPointerException e) {
            System.out.println("of(null) throws NPE");
        }

        // ofNullable() - value may be null
        Optional<String> fromNull = Optional.ofNullable(null);
        Optional<String> fromValue = Optional.ofNullable("World");
        System.out.println("From null: " + fromNull.isPresent()); // false
        System.out.println("From value: " + fromValue.isPresent()); // true

        // empty()
        Optional<String> empty = Optional.empty();
        System.out.println("Empty: " + empty.isPresent()); // false

        // Practical: returning Optional from methods
        System.out.println("Find 3: " + findNumber("abc3def")); // Optional[3]
        System.out.println("Find 9: " + findNumber("abcdef")); // Optional.empty
    }

    static Optional<Character> findNumber(String s) {
        for (char c : s.toCharArray()) {
            if (Character.isDigit(c)) return Optional.of(c);
        }
        return Optional.empty();
    }
}

Key rules:

  • Never use Optional.of(null) — use Optional.ofNullable() instead
  • Never store null inside Optional
  • Optional is meant to be returned from methods, not stored in fields

Methods

Optional Methods

Optional provides several methods for accessing and transforming values.

import java.util.Optional;

public class OptionalMethodsDemo {
    public static void main(String[] args) {
        Optional<String> present = Optional.of("Hello");
        Optional<String> empty = Optional.empty();

        // isPresent() - check if value exists
        System.out.println("present.isPresent(): " + present.isPresent()); // true
        System.out.println("empty.isPresent(): " + empty.isPresent()); // false

        // get() - get value (throws NoSuchElementException if empty)
        System.out.println("present.get(): " + present.get()); // Hello
        try {
            empty.get();
        } catch (java.util.NoSuchElementException e) {
            System.out.println("empty.get() throws exception");
        }

        // orElse(T other) - return value or default
        String s1 = present.orElse("Default");
        String s2 = empty.orElse("Default");
        System.out.println("present.orElse: " + s1); // Hello
        System.out.println("empty.orElse: " + s2); // Default

        // orElseGet(Supplier) - return value or compute default
        String s3 = empty.orElseGet(() -> "Computed " + 42);
        System.out.println("empty.orElseGet: " + s3); // Computed 42

        // orElseThrow() - return value or throw exception
        try {
            empty.orElseThrow();
        } catch (java.util.NoSuchElementException e) {
            System.out.println("empty.orElseThrow() throws exception");
        }

        // ifPresent(Consumer) - execute action if value exists
        present.ifPresent(val -> System.out.println("ifPresent: " + val));
        empty.ifPresent(val -> System.out.println("This won't print"));

        // ifPresentOrElse(Consumer, Runnable) - Java 9+
        present.ifPresentOrElse(
            val -> System.out.println("Found: " + val),
            () -> System.out.println("Not found")
        );

        // filter(Predicate) - keep if condition met
        Optional<String> filtered = present.filter(s -> s.length() > 3);
        Optional<String> filteredOut = present.filter(s -> s.length() > 10);
        System.out.println("Filtered in: " + filtered.isPresent()); // true
        System.out.println("Filtered out: " + filteredOut.isPresent()); // false

        // map(Function) - transform value
        Optional<Integer> length = present.map(String::length);
        System.out.println("Length: " + length.orElse(0)); // 5

        // flatMap(Function) - transform to another Optional
        Optional<String> flatMapped = present.flatMap(s -> Optional.of(s.toUpperCase()));
        System.out.println("FlatMapped: " + flatMapped.orElse("")); // HELLO
    }
}

Method summary:

Method Returns Description
isPresent() boolean Has value?
get() T Get value (throws if empty)
orElse(T) T Value or default
orElseGet(Supplier) T Value or computed default
orElseThrow() T Value or throw NoSuchElementException
ifPresent(Consumer) void Execute if present
filter(Predicate) Optional Keep if condition met
map(Function) Optional Transform value
flatMap(Function) Optional Transform to Optional

Common Patterns

Common Optional Patterns

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

public class OptionalPatternsDemo {
    public static void main(String[] args) {
        // Pattern 1: Safe chaining
        String result = Optional.ofNullable(getUserCity(null))
            .orElse("Unknown City");
        System.out.println("City: " + result);

        // Pattern 2: Chained operations
        Optional<Integer> safeLength = Optional.ofNullable("Hello")
            .filter(s -> !s.isEmpty())
            .map(String::length);
        System.out.println("Safe length: " + safeLength.orElse(0));

        // Pattern 3: Nested Optional handling
        Optional<String> nested = Optional.ofNullable("  Hello  ")
            .map(String::trim)
            .filter(s -> s.length() > 3)
            .map(String::toUpperCase);
        System.out.println("Nested: " + nested.orElse("default")); // HELLO

        // Pattern 4: Default values
        String value = Optional.ofNullable(null)
            .orElse(getDefaultValue());
        System.out.println("Default: " + value);

        // orElseGet is lazy - only computed if needed
        String lazy = Optional.ofNullable(null)
            .orElseGet(() -> expensiveComputation());

        // Pattern 5: Optional in streams
        import java.util.*;
        import java.util.stream.*;
        List<String> names = Arrays.asList("Alice", null, "Bob", null, "Charlie");
        List<String> filtered = names.stream()
            .filter(Optional::isPresent)
            .map(Optional::get)
            .collect(java.util.stream.Collectors.toList());
        System.out.println("Filtered: " + filtered); // [Alice, Bob, Charlie]

        // Better: use Objects::nonNull
        List<String> better = names.stream()
            .filter(Objects::nonNull)
            .collect(Collectors.toList());

        // Pattern 6: Map lookup with default
        Map<String, Integer> scores = new HashMap<>();
        scores.put("Alice", 95);
        scores.put("Bob", 87);
        int charlieScore = Optional.ofNullable(scores.get("Charlie"))
            .orElse(0);
        System.out.println("Charlie: " + charlieScore); // 0
    }

    static String getUserCity(String user) {
        return null; // simulate null
    }

    static String getDefaultValue() {
        return "default";
    }

    static String expensiveComputation() {
        System.out.println("Computing...");
        return "computed";
    }
}

Best practices:

  • Use orElse() for simple defaults, orElseGet() for expensive computation
  • Chain map() and filter() instead of nested if-else
  • Never call get() without checking isPresent() first
  • Use ifPresentOrElse() for if-else with Optional

Best Practices

Optional Best Practices and Anti-Patterns

import java.util.Optional;
import java.util.List;
import java.util.ArrayList;

public class OptionalBestPractices {
    public static void main(String[] args) {
        // DON'T: Use Optional as a field type
        // BAD: private Optional<String> name;
        // GOOD: private String name;

        // DON'T: Use Optional for primitive collections
        // BAD: List<Optional<Integer>>
        // GOOD: List<Integer> with null checks or IntStream

        // DON'T: Call isPresent() then get()
        // BAD:
        Optional<String> opt = Optional.of("hello");
        if (opt.isPresent()) {
            String value = opt.get(); // redundant
            System.out.println(value);
        }
        // GOOD:
        opt.ifPresent(System.out::println);

        // DON'T: Create Optional with of(null)
        // BAD: Optional.of(null) // throws NPE
        // GOOD: Optional.ofNullable(null)

        // DON'T: Return null from Optional-returning methods
        // BAD: return null;
        // GOOD: return Optional.empty();

        // DO: Use Optional for return types to indicate possible absence
        public Optional<String> findUser(int id) {
            if (id == 1) return Optional.of("Alice");
            return Optional.empty();
        }

        // DO: Chain operations instead of nested checks
        // BAD:
        String name = "Hello";
        if (name != null) {
            String trimmed = name.trim();
            if (!trimmed.isEmpty()) {
                String upper = trimmed.toUpperCase();
                System.out.println(upper);
            }
        }

        // GOOD:
        Optional.ofNullable("Hello")
            .map(String::trim)
            .filter(s -> !s.isEmpty())
            .map(String::toUpperCase)
            .ifPresent(System.out::println);

        // DO: Use orElseThrow for required values
        String required = Optional.ofNullable(null)
            .orElseThrow(() -> new RuntimeException("Value required"));

        // Practical: builder pattern with Optional
        User user = new User.Builder()
            .name("Alice")
            .email(null)
            .build();
        System.out.println("User: " + user);
    }

    static class User {
        private String name;
        private String email;

        private User(String name, String email) {
            this.name = name;
            this.email = email;
        }

        static class Builder {
            private String name;
            private String email;

            Builder name(String name) { this.name = name; return this; }
            Builder email(String email) { this.email = email; return this; }

            User build() {
                return new User(
                    Optional.ofNullable(name).orElse("Anonymous"),
                    Optional.ofNullable(email).orElse("no-email")
                );
            }
        }

        @Override
        public String toString() {
            return "User(name=" + name + ", email=" + email + ")";
        }
    }
}

Summary:

  • Return Optional from methods that may not have a result
  • Chain map/filter/flatMap instead of nested conditionals
  • Use orElse for defaults, orElseGet for lazy computation
  • Never use Optional as a field or parameter type
  • Never return null from Optional-returning methods

Practice Problems

0/1solved
Safe Map Lookup

Write a method `safeGet(Map<String, Integer> map, String key, int defaultValue)` that returns the value for the key, or the defaultValue if the key is not present. Use Optional.

Solution
import java.util.*;
import java.util.Optional;

public class SafeMapLookup {
    public static int safeGet(Map<String, Integer> map, String key, int defaultValue) {
        return Optional.ofNullable(map.get(key)).orElse(defaultValue);
    }
}

Quiz

1. What is the difference between of() and ofNullable()?

Question 1 options

2. What does orElseGet() do differently from orElse()?

Question 2 options

3. What is an anti-pattern when using Optional?

Question 3 options

4. What is the primary purpose of Java Optional?

Question 4 options

Flashcards

Question

What are the 3 ways to create an Optional?

Answer

Optional.of(value) for non-null values, Optional.ofNullable(value) for nullable values, Optional.empty() for no value. Never use of(null).

Question

How do you safely get a value from Optional?

Answer

Use orElse(default) for simple defaults, orElseGet(supplier) for lazy computation, ifPresent(consumer) for side effects. Avoid get() without isPresent().

Question

When should you use Optional in Java?

Answer

Use as return types to indicate possible absence, chain with map/filter/flatMap for null-safe operations. Never use as fields, parameters, or constructor arguments.

Question

What is Java Optional?

Answer

Java Optional is a key concept in Java programming.

Question

When to use Java Optional?

Answer

Use Java Optional when building production systems that require reliability, scalability, and maintainability.

Revision Notes

Key Takeaways

  • 1.Optional.ofNullable(value).orElse(default) replaces null checks
  • 2.Chain map/filter/flatMap for null-safe transformations
  • 3.Never use Optional.of(null) — it throws NPE
  • 4.Use orElseGet for lazy default computation
  • 5.Return Optional from methods, never use as fields

Interview Tips

  • Explain when to use Optional: return types, not fields
  • Demonstrate chained operations: map, filter, flatMap
  • Discuss orElse vs orElseGet (eager vs lazy)
  • Know the anti-patterns: get() without isPresent(), Optional as field type

Cheat Sheet

Optional Cheat Sheet

Creating

  • Optional.of(value) → non-null only
  • Optional.ofNullable(value) → nullable OK
  • Optional.empty() → no value

Accessing

  • isPresent() → has value?
  • get() → value (throws if empty)
  • orElse(default) → value or default
  • orElseGet(supplier) → lazy default
  • orElseThrow() → value or throw
  • ifPresent(consumer) → action if present

Transforming

  • map(Function) → transform value
  • flatMap(Function) → transform to Optional
  • filter(Predicate) → keep if condition

Best Practices

  • Return from methods, not fields
  • Chain map/filter instead of if-else
  • Never of(null), use ofNullable
  • Never return null from Optional methods