Optional
What it is and why use itJuly 4, 2020It's been a while since I did my last dev article. Lots of stuff happening these days that "stress management" is now part of my vocabulary. What's fun though is that this article was supposed to be published last year. And draft notes suggest that it was last worked on a year after I stopped writing dev articles. This could be one of the first drafts that I left unpublished!
Anyways, I'm writing about Option, using Java's Optional
for the examples. Why talk about Option
? Because recent events reminded me of a conversation I had with other developers about Optional
and how they misunderstood its purpose. When they see it as a fancy wrapper around a null-check, it's a cause for concern.
The problem
When we write code, we often do it with only the "happy path" in mind. In the following example, we use getPersonWithId()
, pass it an ID, assume we get a Person
instance, and attempt to call getAge()
on it to get an age value.
public class MyPeopleService {
Person getPersonWithId(int id) {
// return instance of Person when found
// return null when not found
}
void doSomethingWithPersonWithId(int id) {
int age = this.getPersonWithId(id).getAge()
}
}
But when getPersonWithId()
returns a null
, all hell breaks lose! Code gets refactored, developers debate on best practices, people leave, deadlines get pushed, project managers panic, and everyone starts pointing fingers at each other! Or at least that's the scenario that runs in everyone's heads while staying calm and professional. But still! Chaos!
Anyways, this class of error is commonly called a Null Pointer Exception (NPE). It's when our application is written to expect a value, but the value returned was null
. When code receives a null
and starts using it like an object, it causes the application to throw an exception and possibly crash the application.
But it's not entirely the developer's fault though. There are several other problems in this snippet:
null
is a valid value for thePerson
type.- The compiler does not force the developer to deal with a potential
null
.
So how do we deal with such problems then?
Option to the rescue
Option
is a type that represents the presence or absence of a value. The concept is similar to JavaScript's Promise
, which is an object that represents the eventual result of an asynchronous operation. It's a wrapper object essentially.
Below is the same operation above. But instead of getPersonWithId()
returning a value of type Person
, we return an Optional
wrapping a value of type Person
.
public class MyPeopleService {
Optional<Person> getPersonWithId(int id) {
// return instance of Person when found
// return Option when not found
}
void doSomethingWithPersonWithId(int id) {
// Get age of potentially present person
// default to 0 if absent
int age1 = this.getPersonWithId(id)
.map(p -> p.getAge())
.orElse(0);
// Get age of potentially present person
// throw error if absent
int age2 = this.getPersonWithId(id)
.map(p -> p.getAge())
.orElseThrow(PersonNotFoundException::new);
}
}
Optional
solves the first problem by having APIs return one type, an Optional
instead of two kinds values, the value which is of type Persson
or null
. This way, it eliminates the use of null
, allows the caller to expect only one type of value, and prevent the breakage of code between caller and callee.
Optional
solves the second problem this way: To safely extract the value present in an Optional
, it forces us to define the behavior in the event that it is empty. That's right, an Optional
does not give away the wrapped value without either defining a default value or throwing an explicitly-defined exception.
That said, Optional
differs from a regular null-check in that it's a type, it can be type-checked at compile time. It forces us to always handle the empty case, unlike a null check which is often added as an afterthought. Also, it makes you really think of what should happen to the application when it encounters a missing value.
Conclusion
Optional
is somewhat like the lottery. We join the lottery by buying potentially winning or losing tickets. At the same time, we've also implicitly signed the lottery's terms and conditions regarding winning and losing. With all that set, we can now win or lose the lottery without any ambiguity between us and the parties involved.