In this post on the Optional class in Java 8, we will delve into its essence, find its benefits, and see how it changes the way we manage null data. We’ll look at the mechanics of creating and utilizing Optional objects, as well as its many manipulation methods.
What is Optional Class in Java 8?
The Optional class in Java 8 is a container type introduced to address the challenges and risks associated with handling null values.
Optional is a single-value container that either contains a value or doesn't (it is then said to be "empty").
Creating an Empty Optional
Optional emptyOptional = Optional.empty();
Creating an Optional with Value
Optional<Employee> employeeOptional = Optional.of(emp);
The above line may throw NullPointer Exception if emp is null.
We can use ofNullable method to avoid NullPointer Exception, in case emp is null, it will create an Empty Optional
Optional<Employee> employeeOptional = Optional.ofNullable(emp);
isPresent method
isPresent() method is to find out whether a value is present in an Optional object.
Optional<Employee> employeeOptional = Optional.ofNullable(emp);
if(employeeOptional.isPresent()){
System.out.println(employeeOptional.get().getName());
}
ifPresent method
ifPresent method takes a Consumer Type(Functional Interface) as an argument.A Consumer is implemented as a lambda expression:
Optional<Employee> employeeOptional = Optional.ofNullable(emp);
employeeOptional.ifPresent(x -> System.out.println(x.getName()));
orElse method
orElse() method provides a default value if Optional is empty
Optional<Employee> employeeOptional = Optional.ofNullable(emp);
Employee employee = employeeOptional.orElse(new Employee("Rochit"));
orElseGet method
orElseGet method takes a Supplier Type(Functional Interface) as an argument.A Supplier is implemented as a lambda expression:
Optional<Employee> employeeOptional = Optional.ofNullable(emp);
Employee employee = employeeOptional.orElseGet(()->new Employee("Vivek"));
orElseThrow method
orElseThrow() method instead of providing a default value if Optional is empty, throws an exception
Optional<Employee> employeeOptional = Optional.ofNullable(emp);
Employee employee = employeeOptional.orElseThrow(IllegalStateException::new);
filter method
The filter method takes a predicate as an argument. If a value is present in the Optional object and it matches the predicate, the filter method returns that value; otherwise, it returns an empty Optional object.
Optional<Employee> employeeOptional = Optional.ofNullable(emp);
employeeOptional.filter(x -> "Gyan".equals(x.getName())).ifPresent(x -> System.out.println("Gyan is the Employee"));
Transforming Value with map() and flatMap
We can use map and flatMap to transform values.
public<U> Optional<U> map(Function<? super T, ? extends U> mapper){...}
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper){...}
map and flatMap returns the result of the computation wrapped inside Optional. The difference is that map takes an unwrapped argument whereas flatMap takes a wrapped value(Optional value).
Employee empWithAddress = new Employee("Gyan", new Address("Street"));
Optional<Employee> empOptional = Optional.of(empWithAddress);
//Here getAddress() is an unwrapped value
String street= empOptional.map(x->x.getAddress()).get().getStreet();
//Here getAddressOptional() is an wrapped value(Optional Object)
String street1= empOptional.flatMap(x->x.getAddressOptional()).get().getStreet();
Java Code
import java.util.Optional;
public class OptionalExample {
public static void main(String[] args) {
// empty Optional
Optional<Employee> emptyOptional = Optional.empty();
Employee emp = new Employee("Gyan");
//Optional with a non-null value
//If emp were null, a NullPointerException would be immediately thrown
Optional<Employee> employeeOptional = Optional.of(emp);
//If emp were null, the resulting Optional object would be empty.
Optional<Employee> employeeOptionalSafe = Optional.ofNullable(emp);
//You can also use the isPresent() method to find out whether a value is present in an //Optional object.
//In addition, there's a get() method that returns the value contained in the Optional //object, if it is present.
//Otherwise, it throws a NoSuchElementException. The two methods can be combined, as //follows, to prevent exceptions:
if(employeeOptionalSafe.isPresent()){
System.out.println(employeeOptional.get().getName());
}
//You no longer need to do an explicit null check; it is enforced by the type system. If the //Optional object were empty, nothing would be printed.
//Lambda expression for Consumer Type Functional Interface
employeeOptionalSafe.ifPresent(x -> System.out.println(x.getName()));
//Using an Optional object, you can rewrite this code by using the orElse() method, which //provides a default value if Optional is empty
Employee employee = emptyOptional.orElse(new Employee("Rochit"));
System.out.println(employee.getName());
//Lambda expression for Supplier Type Functional Interface
Employee employee2 = emptyOptional.orElseGet(()->new Employee("Vivek"));
System.out.println(employee2.getName());
//he filter method takes a predicate as an argument. If a value is present in the Optional //object and it matches the predicate, the filter method returns that value; otherwise, it //returns an empty Optional object.
employeeOptional.filter(x -> "Gyan".equals(x.getName())).ifPresent(x -> System.out.println("Gyan is the Employee"));
//you can use the orElseThrow() method, which instead of providing a default value if //Optional is empty, throws an exception
try {
emptyOptional.orElseThrow(IllegalStateException::new);
}catch(Exception ex) {
System.out.println("Exception:"+ex);
}
//Transforming Value with map()
//The map method returns the result of the computation wrapped inside Optional
//This makes it possible to apply and chain further operations on the response – such //orElse() here.
//Just like the map() method, we also have the flatMap() method as an alternative for //transforming values.
//The difference is that map transforms unwrapped values whereas flatMap takes a //wrapped value and transforms it.
Employee empWithAddress = new Employee("Gyan", new Address("Street"));
Optional<Employee> empOptional = Optional.of(empWithAddress);
//Here getAddress() is an unwrapped value
String street= empOptional.map(x->x.getAddress()).get().getStreet();
//Here getAddressOptional() is an wrapped value(Optional Object)
String street1= empOptional.flatMap(x->x.getAddressOptional()).get().getStreet();
System.out.println("street using map:"+street);
System.out.println("street using flatmap:"+street1);
}
}
class Employee{
private String name;
private Address address;
public Address getAddress() {
return address;
}
public Optional<Address> getAddressOptional() {
return Optional.ofNullable(address);
}
public void setAddress(Address address) {
this.address = address;
}
public Employee(String name, Address address) {
this.name = name;
this.address = address;
}
public Employee(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Address{
private String street;
public Address(String street) {
this.street = street;
}
public String getStreet() {
return street;
}
public Optional<String> getStreetOptional() {
return Optional.ofNullable(street);
}
public void setStreet(String street) {
this.street = street;
}
}
Output
Gyan
Gyan
Rochit
Vivek
Gyan is the Employee
Exception:java.lang.IllegalStateException
street using map:Street
street using flatmap:Street
Best Practices: Optional Class in Java 8
Here are some best practices to consider when working with Optional
Class in Java 8:
- Use
Optional
Selectively:- Use
Optional
for fields or return types that genuinely can be absent. - Avoid using
Optional
for non-nullable fields, as it adds unnecessary complexity.
- Use
- Prefer Factory Methods:
- Use
Optional.of()
for values you know are non-null. - Use
Optional.empty()
when you explicitly want to represent absence. - Use
Optional.ofNullable()
when dealing with potentially null values.
- Use
- Avoid Nested
Optional
Instances:- Do not nest
Optional
instances within otherOptional
instances. - Return an empty
Optional
instead ofOptional.empty()
within an existingOptional
.
- Do not nest
- Avoid Overusing
isPresent()
:- Instead of constantly checking with
isPresent()
, useorElse()
or other methods to handle presence and absence gracefully.
- Instead of constantly checking with
- Use
orElse()
for Default Values:- Employ
orElse(defaultValue)
to provide a fallback value when a value is absent. - Use
orElseGet(() -> defaultValue)
when generating the fallback value is expensive.
- Employ
- Use
orElseThrow()
for Failures:- Use
orElseThrow()
to throw an exception when a value is absent, indicating a failure case.
- Use
- Favor Functional Chaining:
- Chain
map()
,flatMap()
, and other methods to perform transformations and operations on optional values.
- Chain
- Avoid Side Effects in
orElse()
ororElseGet()
:- Avoid performing side effects (like logging) within
orElse()
ororElseGet()
to ensure consistency.
- Avoid performing side effects (like logging) within
- Keep Streams in Mind:
- If dealing with collections, consider using streams and their built-in null handling instead of
Optional
.
- If dealing with collections, consider using streams and their built-in null handling instead of
- Consider
ifPresent()
:- Use
ifPresent()
when you want to perform an action only if a value is present.
- Use
Conclusion: Optional Class in Java 8
By using the Optional
class in Java 8, programmers can:
- Clearly communicate the potential absence of values in their code.
- Avoid null pointer exceptions by explicitly dealing with absent values.
- Streamline the process of providing default values when a value is absent.
- Chain operations more effectively when working with optional values.
- Foster a more readable and maintainable coding style.
Related Articles: