Java 8 Feature

    Optional Class

    Say goodbye to NullPointerException with elegant null handling

    Optional<String>
    🎁 Value
    isPresent()
    Result
    true ✅
    No NullPointerException! Optional handles absence safely.
    🛡️
    Null Safety
    Prevents NPE
    Expressive API
    Fluent methods
    🔗
    Functional
    Chain operations

    What is Optional?

    Optional is a container object that may or may not contain a non-null value. It's designed to force you to think about cases where a value might be absent, rather than accidentally assuming a value exists.

    💡 Think of it like a gift box:

    An Optional is like a gift box that might have something inside, or might be empty. Before you can use what's "inside", you have to check if there's actually anything there. This is much safer than assuming there's always a gift!

    ❌ The Old Way (Dangerous):

    Java Example
    String name =findUser(id).getName();// If findUser returns null...// NullPointerException! 💥

    ✓ With Optional (Safe):

    Java Example
    Optional<User> user =findUser(id);String name = user
    .map(User::getName).orElse("Unknown");// Safe! Always returns a value

    Creating Optional Objects

    There are three main ways to create an Optional:

    CreatingOptionals.java
    // 1. Optional.empty() - Create an empty Optional// Use when you know there's no valueOptional<String> empty =Optional.empty();// 2. Optional.of(value) - Create Optional with non-null value// THROWS NullPointerException if value is null!Optional<String> name =Optional.of("Alice");// WARNING: This crashes!// Optional<String> bad = Optional.of(null);  // NPE!// 3. Optional.ofNullable(value) - Create Optional that accepts null// Safest choice when value might be nullString possiblyNull =getUserInput();// might be nullOptional<String> maybe =Optional.ofNullable(possiblyNull);// RULE OF THUMB:// - Use of() when you're 100% sure value is not null// - Use ofNullable() when value might be null (most common)// - Use empty() when you explicitly have no value

    ⚠️ Common Mistake:

    Using Optional.of() with potentially null values. This defeats the purpose of Optional since it will still throw NullPointerException! Always useOptional.ofNullable() when the value might be null.

    Checking and Getting Values

    CheckingOptionals.java
    Optional<String> name =Optional.of("Alice");Optional<String> empty =Optional.empty();// ===== CHECKING IF VALUE EXISTS =====// isPresent() - returns true if value existsif(name.isPresent()){System.out.println("Name is: "+ name.get());}// isEmpty() (Java 11+) - returns true if NO valueif(empty.isEmpty()){System.out.println("No name found");}// ===== GETTING THE VALUE =====// get() - returns value, THROWS exception if empty!String n = name.get();// "Alice"// String e = empty.get();  // NoSuchElementException! 💥// ===== BETTER: WITH DEFAULT VALUES =====// orElse() - return value or defaultString n1 = name.orElse("Unknown");// "Alice"String e1 = empty.orElse("Unknown");// "Unknown"// orElseGet() - return value or compute default (lazy!)String e2 = empty.orElseGet(()->lookupDefaultName());// The lambda only runs if Optional is empty// ===== WITH EXCEPTIONS =====// orElseThrow() - return value or throw exceptionString n3 = name.orElseThrow();// "Alice"// empty.orElseThrow();  // NoSuchElementException// Custom exceptionString n4 = name.orElseThrow(()->newIllegalStateException("Name is required!"));

    🎯 orElse() vs orElseGet():

    orElse() always evaluates the default, even if Optional has a value.
    orElseGet() only computes the default if needed.
    Use orElseGet() when the default is expensive to compute!

    Java Example
    // BAD: expensiveOperation() runs even when value exists!
    optional.orElse(expensiveOperation());// GOOD: expensiveOperation() only runs if empty
    optional.orElseGet(()->expensiveOperation());

    Performing Actions on Optional

    OptionalActions.java
    Optional<String> name =Optional.of("Alice");Optional<String> empty =Optional.empty();// ifPresent() - do something only if value exists
    name.ifPresent(n ->System.out.println("Hello, "+ n));// Prints
    empty.ifPresent(n ->System.out.println("Hello, "+ n));// Does nothing// ifPresentOrElse() (Java 9+) - do one thing or another
    name.ifPresentOrElse(
    n ->System.out.println("Found: "+ n),()->System.out.println("Not found"));// A common pattern - instead of:if(name.isPresent()){System.out.println(name.get());}else{System.out.println("default");}// Do this:
    name.ifPresentOrElse(System.out::println,()->System.out.println("default"));

    Transforming Optional Values (map, flatMap, filter)

    The real power of Optional comes from its transformation methods. These let you chain operations without null checks at every step.

    TransformingOptionals.java
    Optional<String> name =Optional.of("  alice  ");// ===== map() - Transform the value =====// If present, apply function. If empty, stay empty.Optional<String> upperName = name.map(String::trim).map(String::toUpperCase);System.out.println(upperName.orElse("?"));// "ALICE"// Chain multiple transformationsOptional<Integer> nameLength = name
    .map(String::trim)// "alice".map(String::toUpperCase)// "ALICE".map(String::length);// 5// ===== filter() - Keep value only if it matches =====Optional<String> longName = name
    .map(String::trim).filter(n -> n.length()>3);// Keep only if length > 3System.out.println(longName.orElse("Too short"));// "alice"// ===== flatMap() - For nested Optionals =====// Use when your function returns an OptionalclassUser{Optional<Address>getAddress(){returnOptional.ofNullable(this.address);}}classAddress{Optional<String>getCity(){returnOptional.ofNullable(this.city);}}// WITHOUT flatMap - nested Optionals (BAD)Optional<Optional<Address>> nested = user.map(User::getAddress);// Yuck! Optional inside Optional// WITH flatMap - flattened (GOOD)Optional<String> city = optionalUser
    .flatMap(User::getAddress)// Optional<Address>.flatMap(Address::getCity);// Optional<String>// Clean! Gets city or empty, no nesting

    💡 map() vs flatMap():

    • map() - Use when your function returns a regular value (T → R)
    • flatMap() - Use when your function returns an Optional (T → Optional<R>)

    Practical Patterns

    OptionalPatterns.java
    // PATTERN 1: Safe method returns// DON'T return null, return Optional!publicOptional<User>findUserById(Long id){User user = database.find(id);returnOptional.ofNullable(user);}// PATTERN 2: Chaining with orElseString displayName =findUserById(id).map(User::getDisplayName).orElse("Anonymous");// PATTERN 3: Fallback to another Optional (Java 9+)Optional<User> user =findInCache(id).or(()->findInDatabase(id)).or(()->findInBackup(id));// PATTERN 4: Convert to Stream (for optional in streams)List<User> users = userIds.stream().map(this::findUserById)// Stream<Optional<User>>.flatMap(Optional::stream)// Stream<User> (empties removed!).collect(Collectors.toList());// PATTERN 5: Conditional logicfindUserById(id).filter(User::isActive).filter(u -> u.getAge()>=18).ifPresent(this::sendWelcomeEmail);

    💡 Best Practices

    ✅ DO:

    • Use as method return type
    • Use with Stream operations
    • Use map/flatMap instead of get()
    • Return Optional.empty() not null

    ❌ DON'T:

    • Use as method parameter
    • Use as class field
    • Use in collections (List<Optional>)
    • Just use isPresent() + get()
    DosAndDonts.java
    // ❌ BAD: isPresent() + get() pattern (defeats the purpose!)if(optional.isPresent()){doSomething(optional.get());}// ✅ GOOD: Use ifPresent()
    optional.ifPresent(this::doSomething);// ❌ BAD: Return null from Optional-returning methodpublicOptional<User>findUser(Long id){if(notFound){returnnull;// NEVER DO THIS!}}// ✅ GOOD: Return Optional.empty()publicOptional<User>findUser(Long id){if(notFound){returnOptional.empty();// Correct!}returnOptional.of(user);}// ❌ BAD: Optional as method parameterpublicvoidprocess(Optional<String> name){}// Awkward for callers// ✅ GOOD: Just accept nullable and handle internallypublicvoidprocess(String name){Optional.ofNullable(name).ifPresent(this::doWork);}// ❌ BAD: Optional as fieldclassUser{privateOptional<String> nickname;// Don't do this!}// ✅ GOOD: Just use nullable fieldclassUser{privateString nickname;// Can be nullpublicOptional<String>getNickname(){returnOptional.ofNullable(nickname);}}

    ⚠️ Common Mistakes to Avoid

    1.

    Calling get() without checking

    Never call get() without ensuring value exists. Use orElse(), orElseGet(), or map() instead.

    2.

    Using Optional.of() with nullable values

    This throws NPE immediately! Use Optional.ofNullable() for values that might be null.

    3.

    Overusing Optional

    Optional is for return types, not fields, parameters, or collections.

    4.

    Returning null from methods that return Optional

    Always return Optional.empty(), never null!

    📝 Quick Summary

    Creating:

    • Optional.of(value) - must not be null
    • Optional.ofNullable(value) - might be null
    • Optional.empty() - explicit empty

    Key Methods:

    • map() - transform value
    • flatMap() - transform and flatten
    • filter() - conditional keep
    • orElse() / orElseGet() - defaults