4 Reasons Why You Should Use Java Optional — or Not?

4 Reasons Why You Should Use Java Optional — or Not?

The battle was fierce. The developers were exhausted.

Arguments were fired both ways. Each side had piles of bodies lying on the ground, burned out or collapsed.

Wars were fought over emails, merge requests, and Zoom Calls, but there was no clear winner. Let’s settle the Battle Of Optional once and for all!

Overview

We can use the Optional class to wrap our data and avoid the classical null checks and some of the try-catch blocks.

As a result, we’ll be able to chain method calls and have more fluent, functional code.

On the other hand, using it abusively can lead to a drop in performance and code cluttering.

In this article, we’ll explore some common use cases where we need protection against null values. For each of them, we’ll decide if Optional can be used to simplify our code or if we should stick to the classical null checks.

PS: For the code snippets, we will use the AccountRepository class with the following methods:

public Account get(String username);
public Optional<Account> find(String username);

1. Optional as Method Return Type 👍

One way of using Optional would be to wrap data before returning it - if it can potentially be null. This approach was quickly adopted by developers and frameworks such as SpringDataJPA.

As a result, the caller will be aware that the result can be null. Furthermore, this gives the caller some flexibility: for example, it allows him to easily throw a custom exception if the Optional is empty.

Furthermore, if we need to perform additional checks on the data retrieved, Optional provides a nice API for it.

Let’s look into some scenarios and compare the two solutions:

We can notice the expression of the if statement is growing and becoming harder to read. For these use cases, we can leverage the filter and map methods from Optional’s API.

2. Wrapping the Getters 👍

As we can notice from the code snippet above, the Account class exposes a getMembershipOptional() method.

Optional<Membership> getMembershipOptional() {
    return Optional.ofNullable(membership);
}

Of course, this is not the actual getter or the membership field. But that’s just because I have used the same data model for all the examples. In a real project, we’ll either have the classic getter or the one returning Optional, but not both.

Making the getter's return Optional can be good practice for the fields where null is a valid value from a business point of view.

Since this would help us enrich our domain model, we should not apply it to DTO objects. This way, we’ll also avoid potential serialization issues.

3. Wrapping Local Variables For Very Simple Logic👎

Wrapping variables in Optional just for leveraging its API for simple operations is starting to become a common anti-pattern.

For example, using Optional just to inline if statements:

Optional.ofNullable(account)
    .ifPresent(acc -> processAccount(acc));

The usage of Optional here is not bringing any value. In this case, we should shamelessly use the classic null check:

if (account != null) {
    processAccount(account);
}

Let’s see one more example where the usage of Optional is unnecessary:

var accountType = Optional.ofNullable(account)
    .map(acc -> acc.getAccountType())
    .orElse(DEFAULT_ACCOUNT_TYPE);

Similarly, we can use a classic if statement or a ternary operator to make this piece of code more readable.

4. Optional Fields and Method Parameters 👎

Optional is not meant to be used to wrap class fields. This will lead to the creation of objects where they are not needed and, if used repeatedly, in a performance drop.

Besides, the Optional class is not serializable. Consequently, having Optional fields can cause serialization issues.

Moreover, using Optional as a method parameter can also be considered an anti-pattern.

Let’s assume we have the following method signature:

createAccount(String userName, Optional<Membership> membership)

If this is the case and the membership parameter can be nullable, I believe it is better to simply overload the method with both versions of it.

This way we can properly check and validate the membership parameter for the method where it’s required:

createAccount(String userName);
createAccount(String userName, Membership membership);

Conclusion

As we have seen together in this article, the Optional class can bring expressivity to our domain model (through getters) and some degree of safety.

Though, if it’s not properly used, it can lead to bad design and code cluttering. Consequently, there are developers sharing the opinion that it should be completely avoided.

My opinion is that with some solid rules established among the team and some proper code reviews, Optional can be a great feature for Java Developers.

Resources

There are plenty of articles on this topic. If you want to dive deeper into the subject, here are, in my opinion, some of the best ones out there:

Thank You!

Thanks for reading the article and please let me know what you think! Any feedback is welcome.

If you want to read more about clean code, design, unit testing, functional programming, and many others, make sure to check out my other articles.

If you like my content, consider following or subscribing to the email list. Finally, if you consider supporting my blog and buy me a coffee I would be grateful.

Happy Coding!

Did you find this article valuable?

Support Emanuel's Blog by becoming a sponsor. Any amount is appreciated!