Monday, April 4, 2016

How to Avoid Null Checks in Java

I suppose every Java developer wrote checks similar to the following many times:
if (obj != null) {
    // ...
    obj.doSomething();
    // ...
}
Sometimes such code seems reasonable and its usage can be justified. However more often than not there are better ways to code which allow for avoiding null checks altogether. In this article I want to present several helpful approaches which can make life of a Java developer easier in this regard. All of them use core Java only. Let's get started.

Using Assertions

Assertions were introduced in Java starting from version 1.4. However it looks like they are still underused. Here is an example which checks via assertions whether an argument belongs into required numerical range:
package net.softwaregeek;

public class Main {
    public static void main(final String[] args) {
        doSomething(-300);
        // ...
    }

    private static void doSomething(final int arg) {
        assert (arg > -100) && (arg < 100);
        // ...
    }
}
In the above example arg is out of range hence AssertionError will be thrown:
Exception in thread "main" java.lang.AssertionError
 at net.softwaregeek.Main.doSomething(Main.java:9)
 at net.softwaregeek.Main.main(Main.java:5)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:606)
 at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
In order to add some details to the AssertionError a message can be attached to the assertion. It should be separated by colon from the assertion condition:
package net.softwaregeek;

public class Main {
    public static void main(final String[] args) {
        doSomething(-300);
        // ...
    }

    private static void doSomething(final int arg) {
        assert (arg > -100) && (arg < 100) : "arg is out of range";
        // ...
    }
}
This time stack trace contains descriptive error message:
Exception in thread "main" java.lang.AssertionError: arg is out of range
 at net.softwaregeek.Main.doSomething(Main.java:10)
 at net.softwaregeek.Main.main(Main.java:5)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:606)
 at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Going back to null checks, the code similar to the following can be used to avoid them:
private void doSomething(final Date date) {
    assert (date != null) : "date cannot be null";
    // ...
}
Parentheses are optional, but in my opinion they improve readability by emphasizing the assertion condition, so I'll keep them.

The last but not the least: assertions are disabled by default. In order to enable them -ea option should be given to the JVM. Assertions can be enabled/disabled globally or per class/package. Take a look here for more information.

I intentionally used private methods in the above examples to emphasize an important point. Since assertions are disabled by default and can be selectively turned on/off it makes sense to use them for testing purposes or in private methods in the environments which you can control. In a production environment assertions can be disabled and they will be ignored by JVM. Hence they don't work well for checking invariants of public methods. More details on this can be found in Item 38 of Joshua Bloch's Effective Java book. Let's take a look at the next option.

Using Objects.requireNonNull()

A lot of improvements and enhancements were introduced in Java 7. One of them is Objects utility class. Among other goodies it contains methods that streamline null checks. Here they are:
public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}

public static <T> T requireNonNull(T obj, String message) {
    if (obj == null)
        throw new NullPointerException(message);
    return obj;
}
As you can see the difference between the latter and the former is an error message which is passed to the exception constructor. So the code similar to the following can be used to make sure the object in question is not null:
public void doSomething(final Date date) {
    Objects.requireNonNull(date, "date cannot be null");
    // ...
}
Looks good, but we can do even better...

Using Optional Type

In Java 8 class Optional was introduced. The idea behind it is known for a long time in the functional programming world. An instance of Optional<T> is either a wrapper for an object of type T or for no object at all. Let's take a look at several usage examples:
package net.softwaregeek;

import java.util.Optional;

public class Main {
    public static void main(final String[] args) {
        doSomething(Optional.of("Hello World"));
        doSomething(Optional.empty());
    }

    private static void doSomething(final Optional<String> str) {
        str.ifPresent(System.out::println);
    }
}
The above example demonstrates Optional#ifPresent method. It takes an implementation of Consumer interface and performs provided action if the Optional instance wraps an object.

If return value is needed then Optional#map method can be used. It takes a Function implementation and returns an Optional instance. Here is an example:
package net.softwaregeek;

import java.util.Optional;

public class Main {
    public static void main(final String[] args) {
        doSomething(Optional.of("   Hello World   "));
        doSomething(Optional.empty());
    }

    private static void doSomething(final Optional<String> str) {
        Optional<String> trimmed = str.map(String::trim);
        trimmed.ifPresent(System.out::println);
    }
}
Another useful method is Optional#orElse. It returns wrapped object if it exists. Otherwise default value (passed as argument) is returned:
package net.softwaregeek;

import java.util.Optional;

public class Main {
    public static void main(final String[] args) {
        doSomething(Optional.of("Hello World"));
        doSomething(Optional.empty());
    }

    private static void doSomething(final Optional<String> str) {
        System.out.println(str.orElse("default value"));
    }
}
Making a default value might take a lot of computational work. In this case it makes sense to delay default value computation until it is really needed. This can be done via Optional#orElseGet method. It takes a Supplier implementation as a parameter which will be invoked only in the case an instance of Optional doesn't wrap some object:
package net.softwaregeek;

import java.util.Optional;

public class Main {
    public static void main(final String[] args) {
        doSomething(Optional.of("Hello World"));
        doSomething(Optional.empty());
    }

    private static void doSomething(final Optional<String> str) {
        System.out.println(str.orElseGet(() -> {
            double result = 0;
            // do some time-consuming calculation
            return String.valueOf(result);
        }));
    }
}
There are other helpful methods implemented in the Optional class. Take a look at documentation for complete details. One final note: make sure the code you write with Optional objects does not resemble the code with null checks:
private static void doSomething(final Optional<String> str) {
    if (str.isPresent()) {
        final String value = str.get();
        // do something with value
    }
}
The code above neglects the true power and beauty of Optional objects. Such coding patterns should be avoided.

Conclusion

The idea of null reference might rarely be in handy (e.g. when coding some low-level stuff in C), but most often it should be avoided. Tony Hoare - the inventor of null reference - said the following about it: "I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years."

I hope this article will help you to avoid a lot of billion-dollar mistakes :)

Thank you for reading!
Andrii

5 comments:

  1. This is very informative blog. Very smart work done by the blogger especially use assertion as well as optional types in Java programming. I am very glad to read this article.

    ReplyDelete
    Replies
    1. Thank you, I'm glad it was helpful for you :)

      Delete
  2. It's a bad idea to use Optional as an argument.

    ReplyDelete
    Replies
    1. It is not a bad idea. It should be used wisely.

      Optional as return values are used much more frequently and I agree that their usage in public APIs as arguments might look odd.

      On the other hand optionals as arguments might be occasionally useful in private methods as an implementation detail when the main point is to dissect a long method into a few smaller ones delegating optional processing logic completely to another method.

      Delete
    2. This comment has been removed by the author.

      Delete