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 nullOptional.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)— useOptional.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()andfilter()instead of nested if-else - Never call
get()without checkingisPresent()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
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()?
2. What does orElseGet() do differently from orElse()?
3. What is an anti-pattern when using Optional?
4. What is the primary purpose of Java Optional?
Flashcards
Question
What are the 3 ways to create an Optional?
Click to reveal answer
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?
Click to reveal answer
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?
Click to reveal answer
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?
Click to reveal answer
Answer
Java Optional is a key concept in Java programming.
Question
When to use Java Optional?
Click to reveal answer
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