Protect Your Code from NullPointerExceptions with Spring’s Null-Safety Annotations

Table Of Contents

NullPointerExceptions (often shortened as “NPE”) are a nightmare for every Java programmer.

We can find plenty of articles on the internet explaining how to write null-safe code. Null-safety ensures that we have added proper checks in the code to guarantee the object reference cannot be null or possible safety measures are taken when an object is null, after all.

Since NullPointerException is a runtime exception, it would be hard to figure out such cases during code compilation. Java’s type system does not have a way to quickly eliminate the dangerous null object references.

Luckily, Spring Framework offers some annotations to solve this exact problem. In this article, we will learn how to use these annotations to write null-safe code using Spring Boot.

Example Code

This article is accompanied by a working code example on GitHub.

Null-Safety Annotations in Spring

Under the org.springframework.lang Spring core package, there are 4 such annotations:

  • @NonNull,
  • @NonNullFields,
  • @Nullable, and
  • @NonNullApi.

Popular IDEs like Eclipse and IntelliJ IDEA can understand these annotations. They can warn developers of potential issues during compile time.

We are going to use IntelliJ IDEA in this tutorial. Let us find out more with some code examples.

To create the base project, we can use the Spring Initializr. The Spring Boot starter is all we need, no need to add any extra dependencies.

IDE Configuration

Please note that not all development tools can show these compilation warnings. If you don’t see the relevant warning, check the compiler settings in your IDE.

IntelliJ

For IntelliJ, we can activate the annotation checking under ‘Build, Execution, Deployment -> Compiler’:

IntelliJ compiler config

Eclipse

For Eclipse, we can find the settings under ‘Java -> Compiler -> Errors/Warnings’:

Eclipse compiler config

Example Code

Let’s use a plain Employee class to understand the annotations:

package io.reflectoring.nullsafety;

// imports 

class Employee {
  String id;
  String name;
  LocalDate joiningDate;
  String pastEmployment;

  // standard constructor, getters, setters
}

@NonNull

Mostly the id field (in the Employee class) is going to be a non-nullable value. So, to avoid any potential NullPointerException we can mark this field as @NonNull:

class Employee {

  @NonNull 
  String id;

  //...
}

Now, if we accidentally try to set the value of id as null anywhere in the code, the IDE will show a compilation warning:

IDE warning for NonNull

The @NonNull annotation can be used at the method, parameter, or field level.

At this point, you might be thinking “what if a class has more than one non-null field?”. Would it not be too wordy if we have to add a @NonNull annotation before each of these?

We can solve this problem by using the @NonNullFields annotation.

Here is a quick summary for @NonNull:

Annotated element Effect
field Shows a warning when the field is null
parameter Shows a warning when the parameter is null
method Shows a warning when the method returns null
package Not Applicable

@NonNullFields

Let us create a package-info.java file to apply the non-null field checks at the package level. This file will contain the root package name with @NonNullFields annotation:

@NonNullFields
package io.reflectoring.nullsafety;

import org.springframework.lang.NonNullFields;

Now, we no longer need to annotate the fields with the @NonNull annotation. Because by default, all fields of classes in that package are now treated as non-null. And, we will still see the same warning as before:

IDE warning for NonNullFields

Another point to note here is if there are any uninitialized fields, then we will see a warning to initialize those:

IDE warning for NonNull

Here is a quick summary for @NonNullFields:

Annotated element Effect
field Not Applicable
parameter Not Applicable
method Not Applicable
package Shows a warning if any of the fields are null for the applied package

@NonNullApi

By now, you might have spotted another requirement, i.e., to have similar checks for method parameters or return values. Here @NonNullApi will come to our rescue.

Similar to @NonNullFields, we can use a package-info.java file and add the @NonNullApi annotation for the intended package:

@NonNullApi
package io.reflectoring.nullsafety;

import org.springframework.lang.NonNullApi;

Now, if we write code where the method is returning null:

package io.reflectoring.nullsafety;

// imports

class Employee {

  String getPastEmployment() {
    return null;
  }

  //...
}  

We can see the IDE is now warning us about the non-nullable return value:

IDE warning for NonNullApi

Here is a quick summary for @NonNullApi:

Annotated element Effect
field Not Applicable
parameter Not Applicable
method Not Applicable
package Shows a warning if any of the parameters or return values are null for the applied package

@Nullable

But here is a catch. There could be scenarios where a particular field can be null (no matter how much we want to avoid it).

For example, the pastEmployment field could be nullable in the Employee class (for someone who hasn’t had previous employment). But as per our safety checks, the IDE thinks it cannot be.

We can express our intention using the @Nullable annotation on the field. This will tell the IDE that the field can be null in some cases, so no need to trigger an alarm. As the JavaDoc suggests:

Can be used in association with @NonNullApi or @NonNullFields to override the default non-nullable semantic to nullable.

Similar to NonNull, the Nullable annotation can be applied to the method, parameter, or field level.

We can now mark the pastEmployment field as nullable:

package io.reflectoring.nullsafety;

// imports

class Employee {

  @Nullable
  String pastEmployment;

  @Nullable String getPastEmployment() {
    return pastEmployment;
  }

  //...
}  

Here is a quick summary for @Nullable:

Annotated element Effect
field Indicates that the field can be null
parameter Indicates that the parameter can be null
method Indicates that the method can return null
package Not Applicable

Automated Build Checks

So far, we are discussing how modern IDEs make it easier to write null-safe code. However, if we want to have some automated code checks in our build pipeline, that’s also doable to some extent.

SpotBugs (the reincarnation of the famous but abandoned FindBugs project) offers a Maven/Gradle plugin that can detect code smells due to nullability. Let’s see how we can use it.

For a Maven project, we need to update the pom.xml to add the SpotBugs Maven Plugin:

<plugin>
  <groupId>com.github.spotbugs</groupId>
  <artifactId>spotbugs-maven-plugin</artifactId>
  <version>4.5.2.0</version>
  <dependencies>
    <!-- overwrite dependency on spotbugs if you want to specify the version of spotbugs -->
    <dependency>
      <groupId>com.github.spotbugs</groupId>
      <artifactId>spotbugs</artifactId>
      <version>4.5.3</version>
    </dependency>
  </dependencies>
</plugin>

After building the project, we can use the following goals from this plugin:

  • the spotbugs goal analyzes the target project.
  • the check goal runs the spotbugs goal and makes the build fail if it finds any bugs.

If you use Gradle instead of Maven, you can configure the SpotBugs Gradle Plugin in your build.gradle file:

dependencies {
  spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.11.0'
}

spotbugs {
  toolVersion = '4.5.3'
}

Once the project is updated, we can run the check using the gradle check command.

SpotBugs provides a few rules to flag potential issues by processing the @NonNull annotation during Maven build. You can go through the detailed list of bug descriptions.

For example, if any of the methods annotated with @NonNull is accidentally returning null, then the SpotBugs check will fail with an error similar to this:

[ERROR] High: io.reflectoring.nullsafety.Employee.getJoiningDate() may return null, but is declared @Nonnull [io.reflectoring.nullsafety.Employee] At Employee.java:[line 36] NP_NONNULL_RETURN_VIOLATION

Conclusion

These annotations are indeed a boon for Java programmers to reduce the possibility of a NullPointerException arising during runtime. Please bear in mind this does not guarantee complete null safety, however.

Kotlin uses these annotations to infer the nullability of the Spring API.

I hope you are now ready to write null-safe code in Spring Boot!

Written By:

Saikat Sengupta

Written By:

Saikat Sengupta

Software engineer, coffee lover, continuous learner, opensource contributor

Recent Posts

Optimizing Node.js Application Performance with Caching

Endpoints or APIs that perform complex computations and handle large amounts of data face several performance and responsiveness challenges. This occurs because each request initiates a computation or data retrieval process from scratch, which can take time.

Read more

Bubble Sort in Kotlin

Bubble Sort, a basic yet instructive sorting algorithm, takes us back to the fundamentals of sorting. In this tutorial, we’ll look at the Kotlin implementation of Bubble Sort, understanding its simplicity and exploring its limitations.

Read more

Quick Sort in Kotlin

Sorting is a fundamental operation in computer science and Quick Sort stands out as one of the most efficient sorting algorithms.

Read more