Introduction
We all write code that we are not proud of. Sometimes we are in a rush, sometimes we say we will definitely come back later and deal with all edge cases, usually we tell ourselves different kinds of lies to cover for our own laziness or lack of enthusiasm.
In this article, we will cover one of my worst enemies when it comes to writing clean code. Of course, I am talking about NullPointerException and null checking as usual practice in Java.
Let us take a step back and understand what is null and where do NullPointerException comes from. In the begging, null was created as a keyword for special use case to define a state when something is missing and is yet to be created.
Variables in Java are separated into primitive and reference types. Null and primitive types have nothing in common because primitives are stored in stack memory.
Quick refresher! Java uses two kinds of memory. Stack and heap. In stack memory Java stores primitives and addresses of reference types.
Reference types are where nulls have their usage and where are all NPE's are created.
If we have a list of Strings as our reference type:
List<String> dummyList = List.of("first","second");
dummyList is saved on the stack and it contains the address where is our list located on heap memory.
If we don’t initialize our list i.e. List dummyList; there is no actual memory location to store on stack and Java will default to null. When we try to work with dummyList we will be greeted with NPE and a stack trace.
Checking for null
To avoid NPE before working with reference types we have to check if our object is initialized. There are lots of ways to deal with this and each is uglier than the previous one if you ask me. Without further ado, let us begin.
The good old If keyword and != operator
public void methodOne(String param) {
if (param != null)
doSomethingWithString(param);
}
This is the most common way of handling nulls in methods, we write it all the time, it gets the job done. Problems arise when there are multiple parameters and there are multiple checks and different paths for different combinations of null (or not null).
Java 7 introduced Objects utility class that helps us deal with null safety.
public void methodTwo(String param) {
if (Objects.nonNull(param))
doSomethingWithString(param);
}
Not much is changed here we just moved our if check to a different class.
If you worked with Spring Boot then you probably know that there is annotation for everything. This one comes from JetBrains, creator of IDEA, and is a complement to IDEs static code analysis tools.
public void methodThree(@NotNull String param){
doSomethingWithString(param);
}
My personal 'favorite' is of course using assert keyword. Assert is added to Java as a check for something that must not happen, when assert check fails its stop the world situation (if -ea flag is used on JVM start). It's rarely used but it's still present in the language.
public void methodTwo(String param) {
assert param != null;
doSomethingWithString(param);
}
If the assert fails AssertionError is thrown and JVM is stopped (again if -ea flag is used). My personal opinion is that running applications should never be stoped like this and there are much more graceful ways of shutting down if necessary.
Working with Optional
We saw how we can check if object is null, more checks mean uglier code, it's getting hard to follow, and it's error-prone. What if we never have to check by adjusting how we write code.
Java 8 brought us many changes, including Optional class among others. This enabled us to transform some of our classes to never return null if something is not present or missing.
For an example we could transform our repository class from:
public Person findById(Long id);
Into
public Optional<Person> findById(Long id);
In the first method, we will have to return null if there is no Person with a given id in our repository but if we use Optional we can return Optional.empty(). Returning Optional allows us to use methods such as map, filter, orElseGet, etc. to transform our object or provide new values without doing null check or risking NPE.
One could argue that similar action can be done for collections, but there is other option.
USING PATTERNS
As null checking is common problem in Java world, best practice for mitigating this issue is formed and its name is Null Object pattern.
Sole purpose of this pattern is to reduce null checks in our code using Object Oriented principles. Here we are depending on inheritance and decoupling implementation details from interfaces. To explain the Null Object pattern lets imagine following class structure:
In our Service class we have a method that creates a side effect of sending a notification. At the time of writing this class, we only know that we will send emails but there should be room for more types of messages, so we create NotificationSender interface and two implementations email and nullSender. If we need to send an email we will, obviously, use EmailNotificationSender implementation but for any other case, we will use NullSender implementation. This way our Service class never checks if NotificationSender implementation for our case exists since there is a fallback option that does not produce anything.
Let us return to our Person repository example and introduce filter method as follows:
public List<Person> filterByName(String name);
Client calls this method expecting all Person objects that have given name, this can be 0 or more objects. Returning empty list instead of null will be according to Null Object pattern.
Using Null Object pattern we tell client what is default behaviour and don’t expect it to handle it by itself (Tell-Don’t-Ask principle).
In the end, null is not your worst enemy and there are situations when you need it, these are a couple of ways that I try to avoid using it.
If you got this far, thanks for reading! You are awesome and happy coding!
Top comments (0)