Skip to main content

Null Exception

·3 mins

Besides being a boy scout, I’m also a code minimalist and believe that good design will take you much further, than being able to produce the one-liner of the century. One reason for this belief is that with good design come sensible defaults and mechanisms that will help you keep your code clean and more safe by default. Today I will talk about null and why I think it should be avoided like the plague, or at the very least be treated like an exception.

Why null is bad #

Look through any random piece of software written in Java. What do you find? My guess is lots and lots of defensive statements like if (var == null). Just focus on the code for a bit, look at the amount of effort expended on checking for null, making sure we have null, making sure we don’t have null. Is that what you would call effective code?

Why null is an exception #

I know of course it’s not, but let me argue for why it should be treated as such. Does null represent an error or at least a warning in your program? Do you have to handle a null-case if it occur? In most cases, this is true, so why then would you treat null any different from an exception?

Null should be handled as soon as possible and then turned into something more manageable, like empty.

Strategies to reduce the need for null #

Collections #

This is the simplest, don’t allow your code to return null anywhere where it should return a collection. Go back and read How empty can you be? for a way to handle these cases with empty instead. Don’t forget to reduce returns to a single one, like below:

List<String> getStringList() {
  return getStringsFromDb()
    .stream()
    .findAny()
    .orElse(Collections.emptyList());
}

or if your underlying layer must return null:

List<String> getStringList() {

  List<String> stringList = getStringsFromSomePlace();

  if (stringList == null) {
    stringList = Collections.emptyList(); // Java 5+
    stringList = List.of(); // Java 9+
  }

  return stringList;
}

Objects #

For single objects such as pojos, beans or other singlets, there are multiple strategies that you can use, but I prefer Optional:

Optional<String> getString() {
  String data = getStringFromSomePlace();

  return data == null ? Optional.empty() : Optional.of(data);
}

Properties and members #

Again, there are a multitude of options to use, but the defense I prefer is to use Objects.requireNonNull() in your setters and your constructors:

public Bean {
  private String data1;
  private String data2;
  private Boolean isRequired;

  public Bean(String data1, String data2, Boolean required) {
    Objects.requireNonNull(data1);
    Objects.requireNonNull(data2, "data2 cannot be null");
    Objects.requireNonNull(required);
  }
}

Conclusion #

You cannot always avoid null checks, but you can minimize the use of them by catching null early, and just like exceptions handle them properly.