Handling null in Java
Its a problem I encouter in most JEE projects I’ve worked on so far. Handling null-values. But why is this a problem? And what strategies can we follow to reduce the problem? That is what I’m trying to find out in this post.
Lets start with a piece of business logic, in a world where we don’t have null-values:
public BigDecimal getBalance(Person person) {
Set<Account> accounts = person.getAccounts();
BigDecimal totalBalance = BigDecimal.ZERO;
for(Account account: accounts) {
totalBalance = totalBalance.add(account.getBalance());
}
return totalBalance;
}
This looks good and understandable! But this isn’t what I see in most projects. Usually I see code like this:
public BigDecimal getBalance(Person person) {
if(person != null) {
Set<Account> accounts = person.getAccounts();
if(accounts != null) {
BigDecimal totalBalance = BigDecimal.ZERO;
for(Account account: accounts) {
if(account != null) {
totalBalance = totalBalance.add(account.getBalance());
}
}
}
return totalBalance;
} else {
return null;
}
}
Wow, that is not a pretty sight, not at all! What can we do about it and how did it happen?
Inversion of logic
The first strategy we can follow is based on inversion of logic which I’ve blogged about before. The idea is that we exit early, this will improve the readability of our method. Lets see that happens to our method is we follow this pattern:
public BigDecimal getBalance(Person person) {
if(person == null || person.getAccounts() == null) {
return null;
}
Set<Account> accounts = person.getAccounts();
BigDecimal totalBalance = BigDecimal.ZERO;
for(Account account: accounts) {
if(account != null) {
totalBalance = totalBalance.add(account.getBalance());
}
}
return totalBalance;
}
This is somewhat better, a bit shorter, but we still have all the ‘!= null’ and ‘== null’ checks we don’t want.
Code by contract
The best way to get rid of null-checks is to get rid of nulls in your applicaties. Just don’t return a null in all of the methods…! This sounds very easy and straight forward, but it is a bit harder then it sounds because it has become such a habbit.
The good thing is that current IDE’s are implementing the @NotNull and @Nullable annotations. With these annotations you can tell other programmers, your IDE and static code analysis tools what your idea was when creating a method:
@Nullable
public Person getPerson(Long id) {
return something.retrievePerson(id);
}
public void printPersonName(Long id) {
Person person = getPerson(id);
System.out.println(person.getName());
//Causes warning: getPerson is Nullable, thus this is a possible NPE!
}
It also helps you to clearly state your assumed preconditions:
public void printPersonName(@NotNull Person person) {
System.out.println(person.getName());
//Very good, we know we won't get a NPE here!
}
public void executeThis() {
Person person = null;
printPersonName(person);
//Causes warning: person might be null, thus can cause a NPE!
//Code analysis tools and/or IDE will warn you about this.
}
It will also help you correct possible coding errors:
@NotNull
public Person getPersonFromDatabase(@NotNull Long id) {
//Use JPQL
Query query = em.createQuery("SELECT p FROM Person p WHERE p.id = :value");
q.setParameter("value", id);
return q.getSingleResult();
//The IDE will complain about this.
//The database might return null, we don't allow returning null in this method.
}
Using this method you have some more certainties. But it isn’t a silver-bullet on its own. We have to stop and think, where do the null-values come from?
Actually, when you stop returning null (which is entirely up to you and your team) there are still situations which are ‘unchecked’. Namely external API’s, frameworks, the ORM-mapper you are using. So everywhere where you execute methods that you haven’t written, you still have to do manual checks. But make sure you do this right away. Then further on in the code, you don’t have to check anything because of your @NotNull-contracts.
If you do this to the Person object above, and all its fields, you will end up with the beautiful clean code of the first example. No checks, its just always filled by contract. The only thing you want to add is information about the parameters:
@NotNull
public BigDecimal getBalance(@NotNull Person person) {
Set<Account> accounts = person.getAccounts();
BigDecimal totalBalance = BigDecimal.ZERO;
for(Account account: accounts) {
totalBalance = totalBalance.add(account.getBalance());
}
return totalBalance;
}
In my experience this works very well, I’ve done this a couple of times, even before the @NotNull and @Nullable existed. Before this we would just add the information in our Javadoc. But with the IDE checking for it this has become a lot easier to use.
Null object pattern
A whole different approach then coding-by-contract is the use of a null-object. The idea behind this pattern is that you don’t return null, but instead you return a real object. In our example we would do the following:
public interface Person {
String getName();
void setName(String name);
List<Account> getAccounts();
//..etc..
}
public class NullPerson implements Person {
public String getName() {
return "";
}
public void setName(String name) {}
public List<Account> getAccounts() {
return Collections.emptyList();
}
}
The huge advantage is that you can always safely call “person.getName()” and “person.getAccounts()” because even if you have a NullPerson the object still exists. This Null-Object is obviously usually a singleton.
public BigDecimal getBalance(Person person) {
Set<Account> accounts = person.getAccounts();
//NullPerson returns empty list, no more NPE!
BigDecimal totalBalance = BigDecimal.ZERO;
for(Account account: accounts) {
totalBalance = totalBalance.add(account.getBalance());
}
return totalBalance;
}
A more elaborate but general version of this pattern is the special case pattern, named by Martin Fowler. Instead of just having a Null-special case you could also develop:
public class UnknownPerson implements Person {
public String getName() {
return "";
}
public void setName(String name) {}
public List<Account> getAccounts() {
return Collections.emptyList();
}
}
public class InvalidatedPerson implements Person {
public String getName() {
return "";
}
public void setName(String name) {}
public List<Account> getAccounts() {
return Collections.emptyList();
}
}
Now you can return much more information then just a meaningless “null”!
There is just one problem with this pattern. It also doesn’t just solve your problems. Why do we want to calculate the total balance of a NullPerson? The moment you retrieve a person and it is an instance of NullPerson, catch it and handle the situation appropiatly, don’t just continue.
Safe null operator
For a time there was speculation that the Safe-null-operator would make its way into Java 7 through Project Coin. If you’ve programmed in Groovy you might have seen it before:
public String getFirstLetterOfName(Person person) {
return person?.getName()?.substring(0, 1);
}
The idea is that you use “?.” instead of “.”. The questionmark means: “If the variable we’re calling is null, the result is null, else, call the next method”.
So in this case:
- If person is null: return null, else:
- If getName() is null: return null, else:
- Return result of substring(0, 1)
If you would now write this code it would be:
public String getFirstLetterOfName(Person person) {
if(person == null) {
return null;
}
if(person.getName() == null) {
return null;
}
return person.getName().substring(0, 1);
}
You could also give a default value:
public void printName(Person person) {
System.out.println(person?.getName() ?: "Anonymous");
}
If person or getName() produce a null the “?:” operator will return the default String.
Sounds pretty good eh? The only problem is that this new operator didn’t make the final list of changes for Java 7.
Fun fact: Do you know why the “?:”-operator is called the “Elvis”-operator? When viewed from the side, as smiley , it looks like Elvis. Including the big curl in his hair.
Category: Java Programming | Tags: Java Programming, null | Comments (10)10 Responses to “Handling null in Java”
Leave a Reply
Nice post. You can also use the Option/Maybe monad.
I’ve did a presentation on Null Handling some time ago, which also describes the Option monad.
http://codemonkeyism.com/better-null-handling-strategies-for-java/
And easier usage with the “for” hack in Java:
http://codemonkeyism.com/for-hack-with-option-monad-in-java/
Cheers
Stephan
This is a really nice article, simple and very useful!
Or….
if null is not a valid input (person being null is a bug), first line in the method is
throw new RuntimeException(“person cannot be null!”);
Adding RuntimeExceptions for all input as check would take quite a bit of effort if you need to do this all over your code. That’s why I still prefer code-by-contract.
Another (probably better) option would be to add assertions (‘assert’ keyword). That is exacly why they created them.
This is cool post dude nice article!!!
If NULL is not a valid value (i.e., always a bug), then the @Nullable and @NotNull annotations make sense. If NULL *is* a valid value, however, then IMHO it is better to handle explicitly in code using IF statements.
The Null object pattern, while convenient, seems dangerous because it can create hard-to-detect bugs. For example, let us assume that, in one case, NULL objects should return a result of NULL (or “unknown person” or “N/A” or…) but in another case they should result in a logic fork. An overworked programmer could easily forget about the logic fork and you get a runtime bug that only shows up in certain situations and whose detection is consequently dependent on having very good testing coverage. And then, assuming you detect it, you have to figure which method has the wrong logic in it.
If you don’t use the Null object pattern, there is a chance you will get NullPointerException, but that’s good because that means the application is fail-fast and you know exactly where you forget to do the check.
Great post about using Null’s in Java. I use Groovy at work and love the elvis operator. Now I can’t live without it sometimes. It really is a shame that Java 7 didn’t get the elvis operator.
This is good stuff. Its funny on face of it Java does not support pointers, yet throws a NullPointer Excpetion. A good programmers always make sure they check for null or at least make sure they have a catch block so that this NPE does not go through the stack. Rest all of these methods work to some extent and which one to choose depends on your context one is writing code under.
[...] con valores nulos es, desde siempre, un dolor de muelas muy importante en el mundo de Java. En este artÃculo de Redcode nos lo recuerdan y proponen algunas de las pocas opciones que [...]
Nice article, but you make the world-with-null values look worse than necessary:
for(Account account: accounts) {
if(account != null) {
totalBalance = totalBalance.add(account.getBalance());
}
}
If the accounts Set is empty, the for loop will not execute its body, so the inner if is not needed:
for(Account account: accounts) {
totalBalance = totalBalance.add(account.getBalance());
}
account will always be set to some non-null value.