NullPointerException - how to avoid the most popular exception in Java?

Rafał Pieńkowski
Calendar icon
15 kwietnia 2021

If you program in Java, you're bound to be familiar with the exception given in the title. Although fixing it is usually easy, the key to success is to avoid the exception before it happens.

A seemingly simple implementation

Suppose we have a user's id data, then we want to retrieve the user (from some database/user repository) and display the user's name in the console. Sound trivial? The first implementation could look like this:

1User user = findUser(userId); System.out.println(user.getName());

Where the findUser method would look like this:

1public User findUser() { return users.get(userId); }

At first glance, everything looks fine. However, what if we get an id that is not in the database? Well, because the findUser method must return an object of type User, so it will return null. You will probably find that it is enough to add a simple check:

1User user = findUser(userId); if (user != null) { System.out.println(user.getName()); }

Actually, it will take care of the issue. However, let's consider... why did we add this check? It happened because we received a NullPointerException. It's just that we would prefer to avoid its occurrence at all. We could try to add a check for every possible variable in the project, but then such checks would make up half of our code, and we don't want that after all.

Optional

With help comes Optional, a class from the not-so-new Java 8. It is a kind of "wrapper" for a variable that may (but does not have to) be a null. So let's change the implementation of the findUser method:

1public Optional<User> findUser(Long userId) { return Optional.ofNullable(users.get(userId)); }

As we can see, we will wrap the User class in the Optional class. The key point here is that the returned type in the method has changed - we have changed the API. The usage can look as follows:

1findUser(userId) .ifPresent(u -> System.out.println(u.getName()));

We have added a more functional character to our code, while what is most important is that now we are not physically able to forget to handle a null - the compiler simply won't allow us to do so.

Useful Optionala methods

In addition to just checking the existence of a given object, we have a rich API at our disposal. Suppose we have a User class with the following fields:

1String name; int age; List<String> roles

Then we would like, based on objects of type User, to extract its name, but only under the condition that the user is an admin and is an adult. An example method that meets these requirements could look like this:

1public String extractAdultAdminName(User user) { return Optional.ofNullable(user) .filter(u -> u.getAge() >= 18) .filter(u -> u.getRoles().contains("ADMIN")) .map(u -> u.getName()) .orElse(""); }

Let's discuss the above example one by one: First, we want to wrap the method argument in Optionala, since we don't know if someone won't pass us a null. Next, the filter(...) and map(...) methods will only execute if Optional is "full". Otherwise, they will be skipped. The filter(...) method, as the name implies, filters the Optional's interior - we will move on if the object sitting inside, meets the given condition. The map(...) method, on the other hand, maps an object to anything else. In the above case, we'll turn a User object into a String. Finally, the orElse(...) method will return the inner object (if it's been there so far), or otherwise, whatever we specify.

That would be almost right, while note that with the above implementation, the absence of an adult admin will result in the return of an empty String. So, at the point of calling this method, we would have to check again that the result is not an empty String. Easy to overlook. So let's improve the above method to the final implementation:

1public Optional<String> extractAdultAdminName(User user) { return Optional.ofNullable(user) .filter(u -> u.getAge() >= 18) .filter(u -> u.getRoles().contains("ADMIN")) .map(u -> u.getName()); }

This way, whoever calls our method will know that they also have to handle the situation when the adult admin is missing.

Summary

If we know that our method can return a null, then let's try to replace the returned type with Optionala. That way later, when using the above method anywhere, we can be sure that we won't forget to handle the null (checking at compile time, not at runtime), and NullPointerException won't appear (or at least not because of it).

Read also

Calendar icon

28 marzec

RABATKA Action - Discover the spring promotion!

The annual spring promotion is underway: RABATKA Action, thanks to which you can save on open training from our proprietary offer.

Calendar icon

8 marzec

Don’t miss the opportunity - register for the IREB exploRE 2024 conference and meet the best experts in the field of requirements

We invite you to the IREB exploRE 2024 conference dedicated to requirements engineering and business analysis. The conference will ta...

Calendar icon

18 styczeń

How to prepare for the ISTQB® Foundation Level exam?

How toprepare for the ISTQB® Foundation Level exam? Check valuable tips before taking the test.