Why should one use Objects.requireNonNull()?

Viewed 164152

I have noted that many Java 8 methods in Oracle JDK use Objects.requireNonNull(), which internally throws NullPointerException if the given object (argument) is null.

public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}

But NullPointerException will be thrown anyway if a null object is dereferenced. So, why should one do this extra null check and throw NullPointerException?

One obvious answer (or benefit) is that it makes code more readable and I agree. I'm keen to know any other reasons for using Objects.requireNonNull() in the beginning of the method.

12 Answers

NPE (Null Pointer Exception) is thrown when you access a member of an object which is null at a later point. Objects.requireNonNull() backtracks from where the value was accessed to where it was initialized as null, thus allowing to focus on real source of NPE.

A simplest scenario:

// Global String
String nullString = null;

Calling the null value:

public void useString() {
    nullString.length();  // The source of exception, without using Objects.requireNonNull()
}

Now if we use Objects.requireNonNull() during initialization:

// Global String
String nullString = Objects.requireNonNull(null); // The source of exception which is indeed the actual source of NPE

Apart from the other answers - to me use of requireNonNull could make a code a bit convenient, (and sometimes easy to read)

For example - lets examine the code below,

private int calculateStringLength(String input) {
        return Objects.
                requireNonNull(input, "input cannot be null").
                length();
    }

This code returns length of the string passed to it as argument - however it will throw an NPE if input is null.

As you can see, with use of requireNonNull - there’s no reason to perform null checks manually anymore.

The other useful thing is the "exception message" is my own hand-written (input cannot be null in this case).

There is also the added benefit that static analysis tools generally know @NonNull and @Nullable for standard library (which is the same as checking for null explicitly) :

public static <T> @NonNull T requireNonNull(@Nullable T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}

The basic usage is checking and throwing NullPointerException immediately.

One better alternative (shortcut) to cater to the same requirement is @NonNull annotation by lombok.

I think it should be used in copy constructors and some other cases like DI whose input parameter is an object, you should check if the parameter is null. In such circumstances, you can use this static method conveniently.

In the context of compiler extensions that implement Nullability checking (eg: uber/NullAway), Objects.requireNonNull should be used somewhat sparingly for occasions when you have a nullable field that you happen to know is not null at a certain point in your code.

In this way, there are two main usages:

  • Validation

    • already covered by other responses here
    • runtime check with overhead and potential NPE
  • Nullability marking (changing from @Nullable to @Nonnull)

    • minimal use of runtime check in favor of compile-time checking
    • only works when annotations are correct (enforced by the compiler)

Example usage of Nullability marking:

@Nullable
Foo getFoo(boolean getNull) { return getNull ? null : new Foo(); }

// Changes contract from Nullable to Nonnull without compiler error
@Nonnull Foo myFoo = Objects.requireNonNull(getFoo(false));

In addition to all the correct answers:

We use it in reactive streams. Usually the resulting NullpointerExceptions are wrapped into other Exceptions depending on their occurance in the stream. Hence, we can later easily decide how to handle the error.

Just an example: Imagine you have

<T> T parseAndValidate(String payload) throws ParsingException { ... };
<T> T save(T t) throws DBAccessException { ... };

where parseAndValidate wrapps the NullPointerException from requireNonNull in a ParsingException.

Now you can decide, e.g. when to do a retry or not:

...
.map(this::parseAndValidate)
.map(this::save)
.retry(Retry.<T>allBut(ParsingException.class))

Without the check, the NullPointerException will occure in the save method, which will result in endless retries. Even worth, imagine a long living subscription, adding

.onErrorContinue(
    throwable -> throwable.getClass().equals(ParsingException.class),
    parsingExceptionConsumer()
)

Now the RetryExhaustException will kill your subscription.

Related