What is Optional? 🤔
According to Oracle: "A container object which may or may not contain a non-null value."
Why use it? 👀
it was introduced in Java 8 to enable us to elegantly handle the Null pointer exception problem.
How?
Let's first start by learning how variables in Java are handled in memory and what NullPointerException means.
In java variables can be one of two types:
-
Primitives
- They are 8 types (int, char, etc...)
- They are saved in the execution stack of the current method.
- Fast info: each method call has its own execution stack.
public void someMethod(){
int x = 10;
}
-
Objects
- They are instances of classes (User, Car, Integer)
- They are saved in the Java Heap, however, references to them are saved in the execution stack of the current method as well.
- Those references are saved into variables.
public void someMethod(){
// the local variable car is a reference to the actual object value in the Heap
Car car = new Car();
}
Where does null fit in the picture? 🖼️
If an object isn't referencing any value in the Heap, then its value is null.
When we try to do some operations using that variable we hit the famous NullPointerException.
Car car;
car.getModel();
How does the Optional help?
It wraps the objects we pass to the calling method, and it provides methods to handle the presence or absence of the null value in a clean code functional style.
Code time 🤖
As an example, let's say we have a Room class, which has a Desk, and that Desk has Pen Holder
First, let's look at some code that doesn't use the Optional class
Room 🏠
@Getter @RequiredArgsConstructor @AllArgsConstructor
public class Room {
private Desk desk;
private final int number;
}
Desk 🗃
@Getter @RequiredArgsConstructor @AllArgsConstructor
public class Desk {
private PenHolder penHolder;
private final String model;
}
Pen Holder 🖊
@Getter @RequiredArgsConstructor
public class PenHolder {
private final int capacity;
}
A service that tells us what is the capacity of the Pen holder in a room, if exists. It has two methods, one that does a null check and another that doesn't
public class FetchingService {
// this method will throw a nullPointerException when
// either the Desk or PenHolder are null
public static int getCapacityOfPenHolder_NoNullCheck(Room room){
return room.
getDesk().
getPenHolder().
getCapacity();
}
// this method performs a null check on both Desk and PenHolder
public static int getCapacityOfPenHolder(Room room){
if (room.getDesk() == null){
return 0;
}
else if (room.getDesk().getPenHolder() == null){
return 0;
}
else return room.getDesk().getPenHolder().getCapacity();
}
}
Now let's apply Optional and see the results
Room 🏠
@AllArgsConstructor @RequiredArgsConstructor @Getter
public class Room {
private Desk desk;
private final int number;
public Optional<Desk> getDeskOpt(){
return Optional.ofNullable(desk);
}
}
Desk 🗃
@Getter @AllArgsConstructor @RequiredArgsConstructor
public class Desk {
private PenHolder penHolder;
private final String model;
public Optional<PenHolder> getPenHolderOpt(){
return Optional.ofNullable(penHolder);
}
}
Pen Holder 🖊
@RequiredArgsConstructor @Getter
public class PenHolder {
private final int capacity;
}
The service
public class FetchingService {
public static int getCapacityOfPenHolder(Room room){
return room.
getDeskOpt().
flatMap(Desk::getPenHolderOpt).
map(PenHolder::getCapacity).
orElse(0);
}
}
The optional code is much more concise, clear, and clean. However, the most important side is that we got rid of the NullPointerException monster.
One thing to be careful of is to not return null instead of the Optional. 🔥
public Optional<PenHolder> getPenHolderOpt(){
if (penHolder == null)
return null;
else return Optional.ofNullable(penHolder);
}
Where to use Optional?
There are a number of options for the place that we might use the Optional wrapper class, for example, wrap method parameters, local variables. However, the most suitable place is on method return types.
public class Desk {
// not good !!!!
private Optional<PenHolder> penHolder;
private final String model;
}
public class Desk {
private PenHolder penHolder;
private final String model;
// Yes !!!
public Optional<PenHolder> getPenHolderOpt(){
return Optional.ofNullable(penHolder);
}
}
Conclusion
The Optional class is a wrapper that wraps objects that might not exist in the Heap (in other words that could be null).
It was created to reduce the possibility of facing a NullPointerException
This class has methods that help us write clean code.