Java runs some of the largest sites and platforms in the world but I’ve often struggled with its design as a language.
And yet it’s a great language for getting things done. I’ve put Java into production where it handles thousands of requests per second while working at companies like Expedia and OpenMarket. The Java compiler, as well as excellent developer tooling in a strong ecosystem surrounding the language, prevents whole classes of application errors.
Too bad, then, that the most common failure seen in a Java application is the NullPointerException. A NullPointerException is thrown whenever the JVM attempts to dereference a variable and finds null instead of an object. How can you prevent that? The answer is easy: just don’t have any variables pointing to null.
Unfortunately, Java is a nasty language that practically forces the programmer into creating (or receiving through method parameters) variables referencing null. Any declared but uninitialized variable automatically references null, and other Java language constructs like try/catch force variables to have to be declared in an outer scope where null is one of the only valid choices.
Here’s a common example, from one of the official Java tutorials, which is considered good Java:
If there were other code that used the out variable later, it would have to perform null-checking (as seen in line 19) again. If out is used frequently, the application might be littered with null-checking.
Workarounds
I have what I think is a solution but I want to briefly mention a couple of the existing ways of handling it, and why I think we still need something else.
Option 1: don’t null check.
One school of thought is just don’t null check. Let the NullPointerException happen.Then take steps to remedy the underlying cause. Robert Brautingham’s Why I Never Null Check Parameters is an example of this philosophy.
I of course agree with this school of thought when it’s actually possible to solve the underlying cause. Unfortunately, I believe that there are plenty of times where the actual language forces null-checking on the programmer. And if the language doesn’t, someone else’s library does.
Photo by Marvin Meyer on Unsplash
Option 2: use Optional.
Another option (ha ha, get it) is to use the Optional class introduced in Java 8. Baeldung’s Guide to Java 8 Optional covers the usage well. Great idea, honestly, if the code in question is handling a Stream. That’s kind of what Optional was made for and if you have the option (ok, ok, I know this is getting a little heavy-handed) go for it.
What I have found, though, is that there are plenty of situations where awkward null-checking has been replaced with still-awkward usage of Optional, such as this example from a popular open-source Java tool:
That’s really no different than the null-check in this twin code:
Other languages like Groovy and C# have a nice null-conditional operator that allows the programmer to specify that a chain of references might contain a null somewhere along the way, and a natural way to deal with that is to just short-circuit the chain of calls and result in a null value.
Here’s the first example from the C# documentation:
Java doesn’t allow operator creation, so we can’t imitate this behavior exactly, but I have used some of the functional features found in Java 8, such as method references, to create similar functionality. Take a look.
As a motivating example, finding a user’s zip code in their account might look similar to this in standard Java:
That’s three null checks in the space of ten lines.
Using my solution, which I’ve released as part of a library called Mill, the motivating example can be converted to:
There would then be a single null check afterward. Or, if an empty string or another default value might be more applicable, there is an alternative getOrDefault() method. There’s also a getOrThrow() method for when an exception is more appropriate.
Photo by Tanguy Sauvin on Unsplash
Mill is made available under the MIT license, so you’re free to grab the jar and put it on your application’s classpath. Mill also has a number of useful stream-related features that can make streams more fluent and readable.
And if you’re the type to contribute, Mill is open to contributions. The repository is available on Github. I’m thinking about getting it distributed so that it’s easy to add to a Maven or Gradle file. But, all things in due time, and with the right help.
A Postscript
I’m sure that as long as Java lives there will be null-checking in Java code. The question is can we do better than what standard Java allows? I believe the answer is yes, we can and I hope that by combining some of the various approaches mentioned in this article, including Mill’s NullSafe class, we can continue to make our application code more elegant and prevent more NullPointerExceptions in Java applications.
Java library to make run-of-the-mill tasks more elegant. Compatible with Java 8+.
mill
Java library to make run-of-the-mill tasks more elegant.
Note: This library is deprecated.
Update March, 2020. I will no longer be maintaining Mill. There are now other libraries with more widespread adoption and some actual funding and a set of active maintainers. (See StreamEx).
In addition, Mill is now the name of a build tool for Java and Scala.
Nevertheless, it is still useful at providing some examples of the things you can add to your own codebase to improve working with Java streams or lambdas.