What Really Happened When I Used Optional In My Java Code
How one small change taught me big lessons about clean and defensive programming in Java

🧠 The Background
Every Java developer has faced it — the dreaded NullPointerException.
A few months ago, while working on a Spring Boot microservice, I was debugging one buried deep inside a chain of service calls. In a moment of optimism, I decided to “clean up” the null checks by wrapping everything with Optional.
“If I use Optional, I’ll never have to deal with nulls again,” I told myself.
Spoiler alert: things didn’t go as planned.
💥 The Mistake
Here’s the innocent-looking code I wrote:
public UserResponse getUserDetails(String userId) {
return Optional.of(userRepository.findById(userId))
.map(User::getProfile)
.map(Profile::getEmail)
.orElse("[email protected]");
}
It looked elegant. But… findById() already returned an Optional!
So I had just created an Optional<Optional<User>> — a nesting nightmare.
The result?
- Some values silently disappeared.
- Others caused exceptions when I chained map() calls.
That’s when I realized: Optional isn’t a magic shield against nulls.
⚠️ What Went Wrong
The problem wasn’t Java’s Optional — It was how I used it.
Here are the key mistakes I made (and many developers still make):
- 🚫 Using Optional for everything
It’s not meant for class fields, constructors, or parameters. It’s designed only for method return types. - 🌀 Wrapping an existing Optional again
This creates Optional<Optional<T>>, breaking your flow logic. - ⚠️ Calling .get() without checking the presence
That’s just replacing one null pointer risk with another. - 🔄 Mixing null and Optional
Either embrace Optional or handle nulls manually — not both.
✅ The Correct Way
After refactoring, here’s what the cleaner, correct version looked like:
public String getUserEmail(String userId) {
return userRepository.findById(userId)
.map(User::getProfile)
.map(Profile::getEmail)
.orElse("[email protected]");
}
Now it’s:
- Simple and readable
- Safe from nulls
- Aligned with Java best practices
This code expresses intent — “email may not be present, and that’s fine.”
🧩 Real-World Example from a Payment System
While working on a digital payment orchestration platform, we handled many partial responses from third-party APIs (e.g., payment providers, loyalty services).
Using Optional helped simplify messy null checks:
String loyaltyPoints = Optional.ofNullable(loyaltyResponse)
.map(LoyaltyResponse::getData)
.map(Data::getPoints)
.map(String::valueOf)
.orElse("0");
vs. the old way:
if (loyaltyResponse != null && loyaltyResponse.getData() != null
&& loyaltyResponse.getData().getPoints() != null) {
loyaltyPoints = String.valueOf(loyaltyResponse.getData().getPoints());
} else {
loyaltyPoints = "0";
}
The Optional version was shorter, safer, and easier to maintain — as long as it wasn’t overused.
💬 Key Takeaways
✅ Use Optional correctly:
- ✔ For return types, not for fields or method parameters.
- ✔ Chain with map, flatMap, and filter.
- ✔ Use orElse, orElseGet, or ifPresent safely.
- ✔ Keep your code readable — not “functional for the sake of functional.”
🚫 Avoid:
- ❌ Optional inside Optional
- ❌ Optional with null assignments
- ❌ Optional in entities (especially JPA)
✨ Final Thoughts
Optional It is one of Java’s most misunderstood yet powerful tools.
It’s not about removing nulls — it’s about making nullability explicit.
When used right, it improves readability, reduces null checks, and adds clarity.
When misused, it can silently introduce complexity and confusion.
Use Optional not to avoid nulls, but to embrace uncertainty gracefully.
💬 Have you faced issues Optional in your projects?
Share your story — every developer has an “Optional moment.” 😄
💡 What Really Happened When I Used Optional In My Java Code was originally published in Javarevisited on Medium, where people are continuing the conversation by highlighting and responding to this story.
This post first appeared on Read More

