Skip to Content
Course content

76: Optional for Null Safety

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I can't tell you how many times I've been woken up at 3 AM because of a NullPointerException. It’s the classic Java rite of passage. The problem usually isn't that we forgot a null check once; it's that in a large codebase, it's impossible to remember which methods return null and which don't. You're basically playing a guessing game with the API.

Today, we're going to look at Optional. It's not a magic wand that deletes nulls from the language, but it's a way of telling the person using your code: "Hey, this value might not be here. You must handle that case before you can touch the data."

Dealing with the dread of NullPointerException

Let's imagine we're building a simple user profile system. Some users provide a bio, and some don't. In the old days, we'd just return null for the bio if it was missing.

public class UserProfile {
    private String username;
    private String bio; // This could be null

    public UserProfile(String username, String bio) {
        this.username = username;
        this.bio = bio;
    }

    public String getBio() {
        return bio;
    }
}

The problem is that the getBio() method signature is a lie. It says it returns a String, but it actually returns "a String or nothing." If I'm a developer using your class and I forget to write if (profile.getBio() != null), my app crashes. I think it's time to be honest about our return types.

Explicitly signaling absence with Optional

By wrapping the return type in an Optional<String>, we change the contract. Now, the compiler essentially forces the caller to acknowledge the possibility of emptiness.

import java.util.Optional;

public class UserProfile {
    private String username;
    private String bio;

    public UserProfile(String username, String bio) {
        this.username = username;
        this.bio = bio;
    }

    public Optional<String> getBio() {
        return Optional.ofNullable(bio);
    }
}

I used Optional.ofNullable() here because the internal bio field is still a regular String that could be null. This method handles that transition perfectly: if bio is null, it returns Optional.empty(); otherwise, it wraps the value.

Wait, I just broke it again

Here is where I usually trip up when I'm rushing. I'll implement Optional and then, out of habit, I'll do something like this in my main logic:

UserProfile profile = new UserProfile("JavaDev", null);
// I'm so excited to get the bio that I just call .get()
String bio = profile.getBio().get(); 
System.out.println(bio);

This is a huge mistake. Calling .get() without checking if the value exists throws a NoSuchElementException. I've essentially just traded a NullPointerException for a different exception. It's the same mistake, just with a fancier wrapper. In a real code review, I'd tell you to almost never use .get() directly.

Handling the value gracefully

The real power of Optional comes from the functional methods that let us define a fallback or a specific action. Let's fix my mistake using orElse() for a default value, and ifPresent() for a conditional action.

UserProfile profile = new UserProfile("JavaDev", null);

// 1. Provide a sensible default if the bio is missing
String bioDisplay = profile.getBio().orElse("This user hasn't written a bio yet.");
System.out.println("Bio: " + bioDisplay);

// 2. Only perform an action if the value actually exists
profile.getBio().ifPresent(b -> System.out.println("Bio length is: " + b.length()));

I much prefer this style. The logic is declarative. Instead of nested if-statements checking for nulls, I'm describing what should happen in each scenario. It makes the code cleaner and, more importantly, it makes it impossible to accidentally trigger a null pointer crash at runtime.




πŸ“‹ Practical Task

Refactoring the Product Discount Logic for Null Safety

You are working on an e-commerce system. The Product class has a discountCode field, but not all products have one. Currently, the code is prone to NullPointerException because it returns a raw String.

Your Task:

  • Modify the getDiscountCode() method in the Product class to return an Optional<String>.
  • In the Main class, remove any manual null checks.
  • Use .orElse() to print "NO_DISCOUNT" if the code is missing.
  • Use .ifPresent() to print "Applying discount: [code]" only if a code exists.
import java.util.Optional;

class Product {
    private String name;
    private String discountCode;

    public Product(String name, String discountCode) {
        this.name = name;
        this.discountCode = discountCode;
    }

    // TODO: Change return type and wrap discountCode in an Optional
    public String getDiscountCode() {
        return discountCode;
    }
}

public class Main {
    public static void main(String[] args) {
        Product p1 = new Product("Laptop", "SAVE10");
        Product p2 = new Product("Mouse", null);

        // TODO: Use .orElse() to print the code or "NO_DISCOUNT"
        System.out.println("P1 Code: " + p1.getDiscountCode());
        System.out.println("P2 Code: " + p2.getDiscountCode());

        // TODO: Use .ifPresent() to print "Applying discount: [code]" for both products
        // (Should only print for p1)
    }
}
Rating
0 0

There are no comments for now.

to be the first to leave a comment.