Java: Null Checks Everywhere - Does Your Code Have Boundary Issues?
Null references were Tony Hoare’s notorious “billion-dollar mistake”. However, as Java developers we are used to living with them. They were there from the start and there are ways to deal with them, although some of them border on the neurotic. Recently, we have seen a lot of code like this:
The thought process of the developer could have been: “When someone passes
null and I dereference this parameter, it will cause a
NullPointerException. I need to handle this! I better code defensively.”
And this is a valid view — in the right context. It makes perfect sense to validate input data, if our team does not control the call site. If our code is used by some other party (e.g. we are implementing the API of a library or a web service), we shouldn’t trust any data that is passed in. We should validate the heck out of it.
Our applications — ideally — have a well-defined interface with the surrounding world. Anything outside is not under our control and cannot be trusted. This means there is a boundary, a danger zone, a part of our application that has to deal with untrusted data. This is the place where we have to validate everything and make the incoming data conform to our internal representation. But the paranoia should end here.
The rest of our application works only with our internal representation. This representation is entirely under our control and we design it to maintain invariants, e.g. certain references not being
null. This results in a safe zone, where we can trust that data integrity is upheld. After all, we wouldn’t pass ourselves a
null pointer here, would we?
It brings no additional value to reassert a non-nullability invariant again and again. It is a waste of time and resources.
One argument we heard for
assertNotNull() in the safe-zone was, that the assertion method can log a more useful error message than what an application server would log in case of a
NullPointerException. However, what is the point of spending effort to comfortably detect
null references at a later stage, instead of preventing them in the first place? After all, why else do we write tests?
We shouldn’t expect
NullPointerException deep in our application code. They should be genuinely surprising to us. Expecting them suggests, that we did not spend enough time on designing our application or communicating that design in a way that every team member understands.