DEV Community

Cover image for Better Null-Checking in Java
scottshipp
scottshipp

Posted on

Better Null-Checking in Java

Photo by Estée Janssens on Unsplash

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
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:

String pathsStr = get(PATHS_KEY);
if (pathsStr != null) {
  ObjectMapper objectMapper = new ObjectMapper();
  try {
    String[] paths = objectMapper.readValue(pathsStr, String[].class);
Enter fullscreen mode Exit fullscreen mode

Examples like these have led a lot of the Java community to declare that Optional.get is a Code Smell.

A Better Way

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
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.

GitHub logo scottashipp / mill

Java library to make run-of-the-mill tasks more elegant. Compatible with Java 8+.

mill Build Status

logo

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.

Compatible with Java 8+.

In this README

How to add Mill to your application

If you are using Maven or another supported dependency management tool, you can use Jitpack to add Mill to your application.

Maven

First, add Jitpack (if you haven't already) to the repositories…

Top comments (13)

Collapse
 
evanoman profile image
Evan Oman • Edited

How is your Nullable example different than using Option.ofNullable?

String zipCode = Optional.ofNullable(user)
                   .map(User::addresses)
                   .map(UserAddresses::billingAddress)
                   .map(Address::zipCode)
                   .orElse("..."); // or .orElse(null) or .get() w/ exception handling
Collapse
 
evanoman profile image
Evan Oman

Ahh, I missed the intermediary null checks, this is more accurate:

String zipCode = Optional.ofNullable(user)
                   .flatMap(user -> Optional.ofNullable(user.addresses()))
                   .flatMap(addresses -> Optional.ofNullable(addresses.billingAddress())
                   .flatMap(address -> Optional.ofNullable(address.zipCode())
                   .orElse("...");
Collapse
 
goyo profile image
Grzegorz Ziemonski

No, you didn’t. You can do it exactly as in your first example.

Thread Thread
 
redaalaoui profile image
Réda Housni Alaoui

I confirm, the first example works.

But besidd that, this goes against rule of Demeter.

You should not have to access so many indirect attributes to get the zip code. User should expose a getZipCode method.

Collapse
 
ernir profile image
Eiríkur Ernir Þorsteinsson

Tip: the first case, where null checks are used to handle resources, can usually be avoided by using a try-with-resources statement, available since Java 7. An example is available in the preceding section of the Java tutorial that is linked.

Collapse
 
ondrejs profile image
Ondrej

Nice post, mill is IMHO super useful library for Java programmers (for those who did not switch to Kotlin yet or maintaining legacy code written in Java :)).

Collapse
 
jbristow profile image
Jon Bristow

I’m of the opinion that if you can’t sell switching to Haskell, then push for kotlin. It just makes writing JVM code more sane.

The only wackiness comes when you run into a library that is cheating with reflection to overcome the lack of sum or product types in Java. (I’M LOOKING AT YOU, GREMLIN!)

Collapse
 
ondrejs profile image
Ondrej

Ha, nice point, Jon,

to be fair, I have to admit that I'm Kotlin enthusiast...but still write much of my code in Java because of.....just because. I have a reasons for it.

Collapse
 
_hs_ profile image
HS

And why not Groovy or Scala instead of Kotlin? TIOBE index shows Groovy(17 today used to be way below) getting way above Kotlin nowadays while the Kotlin gets lower (39 used to be about 35 I think).

Also I played with Kotiln and dislike it as do some other people that use JVM stuff not only me, so why would you assume Kotlin would be better choice?

I really need someone to explain to me what's so great about this language that Groovy or Scala couldn't provide. The only thing is Google and Android in my opinion.

Collapse
 
_hs_ profile image
HS

Nice lib. I do miss ?. and ?? from C# in Java. But I also miss Micronaut/Spring in C# so it's a no brainer :D. Although good thing about JVM frameworks I can combine Groovy and Kotlin with Java to make some stuff have those operators :D.

Collapse
 
byelaw profile image
Christian • Edited

One way of handling nulls which wasn't discussed is to use the following statics used to check objects before operating on them:

Objects.requireNonNull
Objects.requireNonNullElse
Objects.requireNonNullElseGet
Enter fullscreen mode Exit fullscreen mode

I use them early to fail fast if need be, and we can learn more about the exception by adding a second parameter to Objects.requireNonNullto give context.

docs.oracle.com/javase%2F9%2Fdocs%...

There are also the many @notnull annotations available from various libraries such as Spring.

Collapse
 
gustavo94 profile image
Gustavo Preciado

Nice post!

I want to recommend you to look at Option<> monad in VAVR it's behavior is similar to the example of NullSafe.

Collapse
 
qew7 profile image
Maxim Veysgeym

What bout null object pattern?