What are Optional Class and Its Benefits in Java 8?
Optional is a container object that may or may not contain a non-null value. It helps prevent NullPointerException by explicitly handling nullable values.
Creating Optional Objects
import java.util.Optional;
public class CreateOptional {
public static void main(String[] args) {
// 1. Empty Optional
Optional empty = Optional.empty();
System.out.println("Empty: " + empty);
System.out.println("Empty is present? " + empty.isPresent());
// 2. Optional with non-null value
Optional present = Optional.of("Hello World");
System.out.println("Present: " + present);
System.out.println("Present value: " + present.get());
// 3. Optional with nullable value (accepts null)
Optional nullable = Optional.ofNullable(null);
System.out.println("Nullable (with null): " + nullable);
Optional nullableValue = Optional.ofNullable("NonNull");
System.out.println("Nullable (with value): " + nullableValue);
// What happens with null and of()
try {
Optional nullValue = Optional.of(null);
} catch (NullPointerException e) {
System.out.println("Optional.of(null) throws NPE: " + e);
}
// Comparing different creation methods
System.out.println("
=== Comparison ===");
System.out.println("Optional.empty(): " + Optional.empty());
System.out.println("Optional.of("value"): " + Optional.of("value"));
System.out.println("Optional.ofNullable(null): " + Optional.ofNullable(null));
System.out.println("Optional.ofNullable("value"): " + Optional.ofNullable("value"));
}
}
Checking and Retrieving Values
public class CheckOptional {
public static void main(String[] args) {
Optional optional = Optional.ofNullable(getValue());
// 1. isPresent() - check if value exists
System.out.println("=== isPresent() ===");
if (optional.isPresent()) {
System.out.println("Value exists: " + optional.get());
} else {
System.out.println("Value is absent");
}
// 2. isEmpty() (Java 11+)
System.out.println("
=== isEmpty() ===");
System.out.println("Is empty? " + optional.isEmpty());
// 3. ifPresent() - execute if value exists
System.out.println("
=== ifPresent() ===");
optional.ifPresent(value -> System.out.println("Value: " + value));
optional.ifPresent(System.out::println);
// 4. ifPresentOrElse() (Java 9+)
System.out.println("
=== ifPresentOrElse() ===");
optional.ifPresentOrElse(
value -> System.out.println("Value: " + value),
() -> System.out.println("Value is absent")
);
// 5. orElse() - provide default value
System.out.println("
=== orElse() ===");
String orElse = optional.orElse("Default Value");
System.out.println("orElse: " + orElse);
// 6. orElseGet() - provide default using Supplier
System.out.println("
=== orElseGet() ===");
String orElseGet = optional.orElseGet(() -> "Computed Default");
System.out.println("orElseGet: " + orElseGet);
// 7. orElseThrow() - throw exception if empty
System.out.println("
=== orElseThrow() ===");
try {
String result = optional.orElseThrow(() -> new IllegalArgumentException("No value"));
System.out.println("Result: " + result);
} catch (IllegalArgumentException e) {
System.out.println("Exception: " + e.getMessage());
}
// 8. orElseThrow() without parameter (Java 10+)
try {
String result = optional.orElseThrow();
} catch (java.util.NoSuchElementException e) {
System.out.println("NoSuchElementException: " + e);
}
}
static String getValue() {
return null; // or "Actual Value"
}
}
Transforming Optional Values
public class TransformOptional {
public static void main(String[] args) {
Optional name = Optional.of(" John Doe ");
// 1. map() - transform value
System.out.println("=== map() ===");
Optional trimmed = name.map(String::trim);
System.out.println("Trimmed: " + trimmed.orElse("empty"));
Optional length = name.map(String::trim).map(String::length);
System.out.println("Length after trim: " + length.orElse(0));
// 2. flatMap() - transform with Optional result
System.out.println("
=== flatMap() ===");
Optional email = Optional.of("user@example.com");
Optional domain = email.flatMap(TransformOptional::extractDomain);
System.out.println("Domain: " + domain.orElse("unknown"));
// 3. filter() - conditionally keep value
System.out.println("
=== filter() ===");
Optional filtered = name.filter(s -> s.trim().length() > 5);
System.out.println("Name length > 5: " + filtered.orElse("filtered out"));
Optional filtered2 = name.filter(s -> s.trim().length() > 20);
System.out.println("Name length > 20: " + filtered2.orElse("filtered out"));
// Chaining operations
System.out.println("
=== Chaining Example ===");
Optional result = Optional.of(" Java 8 ")
.map(String::trim)
.filter(s -> s.length() > 2)
.map(String::toUpperCase);
System.out.println("Chained result: " + result.orElse("invalid"));
}
static Optional extractDomain(String email) {
if (email != null && email.contains("@")) {
return Optional.of(email.split("@")[1]);
}
return Optional.empty();
}
}
Practical Examples - Before vs After Optional
public class BeforeAfterOptional {
static class Address {
String street;
String city;
String zipCode;
Address(String street, String city, String zipCode) {
this.street = street;
this.city = city;
this.zipCode = zipCode;
}
Optional getCity() {
return Optional.ofNullable(city);
}
Optional getZipCode() {
return Optional.ofNullable(zipCode);
}
}
static class User {
String name;
Address address;
User(String name, Address address) {
this.name = name;
this.address = address;
}
Optional getAddress() {
return Optional.ofNullable(address);
}
Optional getName() {
return Optional.ofNullable(name);
}
}
static class Order {
User user;
double amount;
Order(User user, double amount) {
this.user = user;
this.amount = amount;
}
Optional getUser() {
return Optional.ofNullable(user);
}
}
// BEFORE Optional - null checks everywhere (painful)
public static String getCityBeforeOptional(Order order) {
if (order != null) {
if (order.user != null) {
if (order.user.address != null) {
if (order.user.address.city != null) {
return order.user.address.city;
}
}
}
}
return "Unknown";
}
// AFTER Optional - clean and safe
public static String getCityAfterOptional(Order order) {
return Optional.ofNullable(order)
.flatMap(Order::getUser)
.flatMap(User::getAddress)
.flatMap(Address::getCity)
.orElse("Unknown");
}
public static void main(String[] args) {
// Test with complete data
Address address1 = new Address("123 Main St", "New York", "10001");
User user1 = new User("Alice", address1);
Order order1 = new Order(user1, 100.0);
System.out.println("=== Complete Data ===");
System.out.println("Before Optional: " + getCityBeforeOptional(order1));
System.out.println("After Optional: " + getCityAfterOptional(order1));
// Test with missing address
User user2 = new User("Bob", null);
Order order2 = new Order(user2, 200.0);
System.out.println("
=== Missing Address ===");
System.out.println("Before Optional: " + getCityBeforeOptional(order2));
System.out.println("After Optional: " + getCityAfterOptional(order2));
// Test with missing user
Order order3 = new Order(null, 300.0);
System.out.println("
=== Missing User ===");
System.out.println("Before Optional: " + getCityBeforeOptional(order3));
System.out.println("After Optional: " + getCityAfterOptional(order3));
// Test with null order
System.out.println("
=== Null Order ===");
System.out.println("Before Optional: " + getCityBeforeOptional(null));
System.out.println("After Optional: " + getCityAfterOptional(null));
}
}
Optional Best Practices
import java.util.*;
public class OptionalBestPractices {
// DO: Use Optional for return types that may be absent
public Optional findUserName(int id) {
// Simulating database lookup
if (id == 1) {
return Optional.of("John");
} else if (id == 2) {
return Optional.of("Jane");
}
return Optional.empty(); // Clear that value may be absent
}
// DO: Use Optional for method chain safety
public void chainExample() {
String result = findUserName(1)
.map(String::toUpperCase)
.filter(name -> name.length() > 3)
.orElse("DEFAULT");
System.out.println("Result: " + result);
}
// DON'T: Use Optional as method parameter
// public void processUser(Optional user) - Creates unnecessary wrapping
// DO: Use method overloading instead
public void processUser(String user) {
System.out.println("Processing: " + user);
}
public void processUser() {
System.out.println("Processing: no user");
}
// DON'T: Use Optional.get() without checking isPresent()
public void badExample(Optional opt) {
// String value = opt.get(); // May throw NoSuchElementException
// Better:
String value = opt.orElse("Default");
}
// DO: Use ifPresentOrElse for conditional logic
public void conditionalLogic() {
Optional name = findUserName(3);
name.ifPresentOrElse(
n -> System.out.println("Found: " + n),
() -> System.out.println("Not found")
);
}
// DO: Use Optional for lazy evaluation
public void lazyEvaluation() {
Optional expensive = Optional.ofNullable(getExpensiveValue())
.map(this::expensiveOperation);
// Expensive operation only performed if value exists
}
private String getExpensiveValue() {
System.out.println("Getting expensive value...");
return "value";
}
private String expensiveOperation(String s) {
System.out.println("Expensive operation on: " + s);
return s.toUpperCase();
}
// DO: Use Optional with streams
public void optionalWithStreams() {
List> optionalList = Arrays.asList(
Optional.of("Apple"),
Optional.empty(),
Optional.of("Banana"),
Optional.empty(),
Optional.of("Cherry")
);
// Using filter + get (old way)
List fruits1 = optionalList.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
// Using flatMap (better way, Java 9+)
List fruits2 = optionalList.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());
System.out.println("Fruits: " + fruits2);
}
public static void main(String[] args) {
OptionalBestPractices demo = new OptionalBestPractices();
System.out.println("=== Best Practices Demo ===
");
System.out.println("Finding user 1: " + demo.findUserName(1).orElse("Not found"));
System.out.println("Finding user 3: " + demo.findUserName(3).orElse("Not found"));
demo.chainExample();
demo.conditionalLogic();
demo.optionalWithStreams();
System.out.println("
=== Optional Anti-patterns to Avoid ===");
System.out.println("1. DON'T: Use Optional.get() without checking");
System.out.println("2. DON'T: Use Optional as field type (not serializable)");
System.out.println("3. DON'T: Use Optional as constructor parameter");
System.out.println("4. DON'T: Use Optional as method parameter");
System.out.println("5. DON'T: Use Optional in collections");
System.out.println("6. DON'T: Return null from method that returns Optional");
System.out.println("7. DON'T: Use Optional for simple null checks");
System.out.println("
=== Do's and Don'ts Summary ===");
System.out.println("✓ DO: Use Optional for return values that may be absent");
System.out.println("✓ DO: Use Optional for chaining operations safely");
System.out.println("✓ DO: Use orElse/orElseGet for default values");
System.out.println("✗ DON'T: Use Optional for fields or parameters");
System.out.println("✗ DON'T: Throw NPE when Optional is empty");
System.out.println("✗ DON'T: Use Optional.get() without checking");
}
}
Master Optional class with Online Learner!
0
likes
Your Feedback
Help us improve by sharing your thoughts
Online Learner helps developers master programming, database concepts, interview preparation, and real-world implementation through structured learning paths.
Quick Links
© 2023 - 2026 OnlineLearner.in | All Rights Reserved.
