Please stop the Java Optional mess!

In many of the projects I’ve worked during the recent years I’ve found quite hard to find developers using Java Optionals in the right way, so I thought it’d be good to show what in my opinion it should be the right approach.

Background

First of all, let’s clarify what the original purpose was for having Optional class in Java.

As many of you may know, Optional class was originally introduced in Guava libraries and widely used for many years but it wasn’t part of the JDK until JDK 8 was released.
The main purpose of the Optional class was to avoid some important issues when writing Java code; however, the root cause of all evil was NullPointerException. Some of the issues that the existence of null in Java brought to us are:

  • How to avoid NullPointerException?
  • Should I do null checks on every method?
  • How can I avoid returning null in a method?
  • Should I pass null values to a constructor?

Let’s go through some of these details briefly, to see what we could do before Optional came to our lives.

Avoid NullPointerException

In most of the cases there’s nothing we can do about it, but there are some tricks like using null-safe methods instead of null-unsafe methods or changing the order in which we do some operations to avoid nulls.

For example, when comparing Strings we could have:

private boolean isRed(String color) {
return color.equals("RED");
}
view raw isRedUnsafe.java hosted with ❤ by GitHub


This initial version would be unsafe, that method could raise a NullPointerException if color variable is null. What we could do in this case is to re-arrange the order to make it null-safe.

private boolean isRed(String color) {
return "RED".equals(color);
}
view raw isRedSafe.java hosted with ❤ by GitHub

In this case, NullPointerException will never be raised independently of the value the client passes in.

Other than that, in most of the cases there’s nothing we can really do to avoid NullPointerException apart from placing null check all over the place, something that is tedious and ugly.

Avoid returning null checks

Returning null in our methods is something really bad, because the client should normally expect it to be safe when the response is read; however, is that always possible?

For methods that are returning collections that’s easy, we just return an empty collection as a rule of a thumb, but what about any other Java object?
One of the approaches that was encouraged in the past was the use of Null Object pattern to avoid doing null checks.
Basically, imagine you have a method that returns a Person object, the idea was to have a NullPerson object to be sent in the cases where the Person instance was null.
It could be a solution, but personally I’m not very convinced about this approach.

Passing null values to constructor

Personally I consider doing this quite nasty, having Optional or not, there’s not really an excuse to pass null to a constructor. There are other solutions that are more intuitive for the client.
I consider that the use of multiple constructors or the use of Builder pattern are a much better solution; using one or the other will depend on the number of optional parameters in the class most of the cases. We won’t discuss them right now because we’d be seeing them later on in this article.

Optional to the rescue

After having had a brief look at what the situation was before Optional existed, now we can see how things look like since Optional was released in JDK 8.

It’s probably widely accepted that Optional has been a good thing for Java developers, as it gives more flexibility and provides the ability of writing a more expressive and flexible code; however, things are not as good as they should. There are some bad patterns in the use of Optional that I think should be clarified and disregarded.

Optional badly done

In the recent years there are two patterns that annoy me the most.

  • Use of Optional in Java fields in our classes
  • Express the optionality of a value in constructors by using Optional

One of the problems of using Optional fields is that Optional is not serialisable. Optional is not meant to be serialised, it’s meant to serve as a wrapper of nullable fields to make it easier to the client to deal with them. Treating Optional as a holder of data is clearly wrong.

The use of Optional as Java fields normally leads to the use of Optional in our constructor values, and this is when things become messy.
Having Optionals all over the place forces developers to initialise Optionals when instantiating classes or when setting up data for our unit tests.
There’s no need for that and the code becomes messy and more verbose than what it’s actually needed.


Let’s look at some code to make it more fun! Let’s imagine that we have this class, which uses Optional in the way we’ve described above.

static class Customer {
private final String firstName;
private final String surname;
private final Optional<Integer> age;
private final Optional<String> address;
private final Optional<String> phoneNumber;
private final Optional<Sex> sex;
public Customer(String firstName, String surname, Optional<Integer> age, Optional<String> address, Optional<String> phoneNumber, Optional<Sex> sex) {
this.firstName = firstName;
this.surname = surname;
this.age = age;
this.address = address;
this.phoneNumber = phoneNumber;
this.sex = sex;
}
public String getFirstName() {
return firstName;
}
public String getSurname() {
return surname;
}
public Optional<Integer> getAge() {
return age;
}
public Optional<String> getAddress() {
return address;
}
public Optional<String> getPhoneNumber() {
return phoneNumber;
}
public Optional<Sex> getSex() {
return sex;
}
}
static enum Sex {
FEMALE,
MALE
}

As you can see, the intention of the person who wrote this class was to express that only firstName and surname were mandatory fields, so the other fields would all be optional.
So now let’s imagine that we wish to declare a Customer instance filling in just the mandatory fields.

final Customer customer = new Customer(
"John",
"Smith",
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty()
);

That’s not ideal, right? Why do we have to set all those Optional to instantiate our class? And even if we wanted to set all those values, it’s quite verbose having to wrap all of them in Optional.

final Customer testCustomer = new Customer(
"John",
"Smith",
Optional.of(30),
Optional.of("Flat 0, Some Street, W1 XYZ"),
Optional.of("01234 567890"),
Optional.of(Sex.MALE)
);

Why do I have to set all those Optional for my instance? It’s even confusing to initialise an instance with an Optional, because I know that in my instance those values are present.

Also, to make things worse, I could even pass a null to any of the Optional fields. This could make the whole solution useless, because the client will still get a NullPointerException!

final Customer customerWithNullOptionals = new Customer(
"John",
"Smith",
null,
null,
Optional.of("01234 567890"),
Optional.of(Sex.MALE)
);
System.out.println(customerWithNullOptionals.getAddress().orElse("No address present!"));

If we run that code, we can see that when we try to access our customer’s address, we raise a NullPointerException! So clearly this approach is not safe either.

So let’s look at how it should be done!

Optional done right

So what do I think we should do instead in these cases? Well, if this class had less fields probably I’d just create different constructors. At least it’d be clear to the client what fields are mandatory by having a look at the available constructors and they wouldn’t have to fill any fields that are not used in the instance.
However, in cases where we have a considerable number of arguments I’d go for a Builder pattern as is cleaner than having too many constructors.

So how would it look like?

import java.util.Optional;
public class Customer {
private final String firstName;
private final String surname;
private final Integer age;
private final String address;
private final String phoneNumber;
private final Sex sex;
private Customer(Builder builder) {
this.firstName = builder.firstName;
this.surname = builder.surname;
this.age = builder.age;
this.address = builder.address;
this.phoneNumber = builder.phoneNumber;
this.sex = builder.sex;
}
public static Builder builderOf(String firstName, String surname) {
return new Builder(firstName, surname);
}
static class Builder {
private final String firstName;
private final String surname;
private Integer age;
private String address;
private String phoneNumber;
private Sex sex;
private Builder(String firstName, String surname) {
this.firstName = firstName;
this.surname = surname;
}
public Builder withAge(Integer age) {
this.age = age;
return this;
}
public Builder withAddress(String address) {
this.address = address;
return this;
}
public Builder withPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
return this;
}
public Builder withSex(Sex sex) {
this.sex = sex;
return this;
}
public Customer build() {
return new Customer(this);
}
}
public String getFirstName() {
return firstName;
}
public String getSurname() {
return surname;
}
public Optional<Integer> getAge() {
return Optional.ofNullable(age);
}
public Optional<String> getAddress() {
return Optional.ofNullable(address);
}
public Optional<String> getPhoneNumber() {
return Optional.ofNullable(phoneNumber);
}
public Optional<Sex> getSex() {
return Optional.ofNullable(sex);
}
static enum Sex {
FEMALE,
MALE
}
}
view raw Customer.java hosted with ❤ by GitHub

Our Customer class will not use Optionals in either the Java fields or the constructor arguments, instead we’ll be expressing to the client the optionality of those fields by returning Optional when those fields are accessed (getters). Also, if the client has to construct an instance it will know very easily what fields are mandatory and it’ll be able to set any other optional field by using the withXXX accessors.

How does it look like when a client has to construct and access our Customer class?

final Customer customer = Customer.builderOf("John", "Smith")
.withAge(30)
.build();
System.out.println(customer.getAddress().orElse("No address present"));

It loos quite simple, right? Constructing an instance doesn’t get polluted now with those unnecessary Optional instances and accessing an Optional field in a safe way is quite easy and clean with the method that the Optional class itself provides.

For those who don’t like the verbosity of having a builder implemented in the class, using Lombok to make the class less verbose could be an option.

One last thing that it’s worth mentioning is that some serialisation libraries have released workarounds to be able to serialise Optional, but I think they’re not doing any good to those developers using it wrongly. As I said before, Optional wasn’t meant to be serialised and putting workarounds in place instead of forcing developers to using it right doesn’t help.

So that’s it! That’s everything I had to show you about what I think it’d be a good use of Optionals in Java, I really hope you’ve enjoyed this reading and please subscribe to my articles if you’re interested in reading more!

Thank you for reading!

2 thoughts on “Please stop the Java Optional mess!

Add yours

Up ↑

Take a look at our recommended books!

Ok!
X
%d bloggers like this: