Optionals Can Ruin Readability of Your Code in Java
What are Optionals?
Optionals have been introduced in Java 8. This concept is borrowed from other languages. Primarily from languages of functional nature like Haskell, F# etc. Optional is a container object which may or may not contain a non-null value.
Why using it?
Let’s have a look at this example where we want to get name of a book retrieved from database:
Book book = bookRepository.findById(id);
String bookName = book.getName();
This code will compile just fine and nothing will not tell you that there is a huge problem. We forgot to check for null. We need to fix this ASAP before someone pushed this code to Production:
Book book = bookRepository.findById(id);
String bookName;
if (book != null) {
bookName = book.getName();
}
Java syntax is not able to help us to identify those places where we unintentionally forgot to place a null-check. This is where Optional might be handy. This is how we can rewrite our example using Optionals:
Optional<Book> bookOptional = bookRepository.findById(id);
String bookName = bookOptional.getName(); // We can't do this!
// the line above produces compilation error
Perfect! Java is able to tell us that there is one step that we’re missing:
String bookName = bookOptional.get().getName();
Again this is still prone to null reference exceptions but at least that extra step that involves using a helper method get(). This extra step is able to remind a developer that there is the case where bookOptional can be null and we need to handle such scenarios.
Optional<Book> bookOptional = bookRepository.findById(id);
String bookName;
if (!bookOptional.isEmpty()) {
Book book = bookOptional.get();
String bookName = book.getName();
}
Seems like we have the magic potion and we can use Optionals everywhere and null reference exceptions will just go away. Unfortunately it’s not that straightforward. The problem that we have that Optionals bloat our code. In Haskell, F# and other programming languages there are built in language constructs that help you utilise the concept of Optionals. And it looks concise and comprehensive there. In Java you have to use helper methods get() and isPresent() repeatedly to achieve the same outcome.
Readability issue
Consider this scenario where we need to build a shelf of book authors.
public Shelf buildShelf(Book book) {
Shelf shelf = new Shelf();
if (book.getAuthorId().isPresent()) {
long authorId = book.getAuthorId().getAsLong();
Optional<Author> authorOptional = repository.findById(authorId);
if (authorOptional.isPresent() && authorOptional.get().getName().isPresent()) {
Author author = authorOptional.get();
String authorName = author.getName().get();
shelf.addAuthorName(authorName);
}
}
return shelf;
}
Let’s have a look at the same version of the code without using optionals:
public Shelf buildShelf(Book book) {
Shelf shelf = new Shelf();
if (book.getAuthorId() != null) {
Author author = repository.findById(book.getAuthorId());
if (author != null && author.getName() != null) {
shelf.addAuthorName(author.getName());
}
}
return shelf;
}
This version seems much easier to read. I pay a lot of attention to the readability of the code that I write. And since I might be coming back to this code in a couple month time — I want to help my future-self and reduce the time that is needed to get an idea of what this code is doing. Also your teammates will thank you for writing easy-to-read code.
Having this in mind — I would be against using Optional in the example above as they make code hard to grasp at a glance. Using Optionals in your internal module which doesn’t expose any contacts to the outside world seems very much like overkill. Overkill in the sense that the price that is paid for reading and maintaining such code is much higher than the benefits.
Good use-case
Use Optionals when you define contact of your library or your layer. Preferably if this library/layer is referenced in many other places. Ideal example would be — your repositories. Return Optional<T> from every method which returns a single object instead of just returning T:
public Optional<Author> findById(long id) {
return findBy(...); // retrieve the author from the database
}
Conclusion
Optionals are baked in into Java. This eases the adoption of this feature. However I encourage you to always apply a fair judgement when you want to use a particular feature of the language.