1. Overview
In this article, we will discuss more on Spring Null-safety annotations along with few examples.
Null-Safety prevents the code that would cause NullPointerException from compilation and forces you to make an explicit decision to prevent it or not to avoid NullPointerExceptions at runtime.
2. Null safety
Java doesn’t enforce Null-safety. But after Java 8, java.util.Optional is included in the language to achieve null safety. Unlike Kotlin, the Java language doesn’t enforce the use of Optional. However, you can use Optional to achieve null safety if you want to.
Besides Optional, Spring 5 also provides a set of annotations to let you declare the nullability of fields and APIs. These annotations are available in the package org.springframework.lang and you can import the annotations from this package.
Annotations are:
- @Nullable
- @NotNull
- @NonNullApi
- @NonNullFields
But these annotations show compile-time errors only in IntelliJ IDEA (v10.5 and above). Refer to More flexible and configurable @Nullable/@NotNull annotations blog post of Jetbrains.
2.1. @Nullable annotation
The @Nullable annotation helps to identify:
- Method that can return null
- Variables (fields, local variables, and parameters) that can be null
Let’s take an example where a variable might be null.
Suppose we want to get user feedback on a product and take necessary action. The user feedback form involves three entries: name, email address, comments.
As a first step, we had to get the user’s comment and check for any SQL injection. Sometimes, the user might submit the form with no comments. In such a scenario, the comment would be null.
It would throw NullPointerException if we try to parse the comment
without checking for null. The below main
function fetches all the feedback submissions using the function generateFeedback()
. It then processes each feedback using listIterator
. Here, the code is not informed that comment
can be null and doesn’t have any null check in place.
public class Runner { public static void main(String[] args) throws InterruptedException { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfiguration.class); context.registerShutdownHook(); ArrayList<Feedback> list = generateFeedback(); ListIterator<Feedback> iterator = list.listIterator(); while (iterator.hasNext()) { Feedback feedback = (Feedback) iterator.next(); String comment = feedback.comment; if (comment.contains(invalidChars)) { } } } } class Feedback { String name; String emailAddress; String comment; Feedback(String name, String emailAddress, String feedback) { this.name = name; this.emailAddress = emailAddress; this.comment = feedback; } public String getFeedback() { return comment; } }
The above code will throw NullPointerException at runtime if the comment is null. To avoid this, we can use @Nullable annotation to show the caller that this comment
variable can be null.
class Feedback { String name; String emailAddress; @Nullable String comment; Feedback(String name, String emailAddress, String feedback) { this.name = name; this.emailAddress = emailAddress; this.comment = feedback; } public String getFeedback() { return comment; } }
2.2. @NonNull annotation
The @NotNull
annotation helps to declare that:
- A method can not return null
- Variables (fields, local variables, and parameters) cannot hold a null value
Let’s take an example to understand this. We have declared employeeId
as NonNull, meaning it can’t accept null values. So when you try to pass a null value, IntelliJ will throw compile-time error.
class Employee { String name; @NonNull String employeeId; Employee(String name, @NonNull String employeeId) { this.name = name; this.employeeId = employeeId; } } public static Employee generateFeedback() { Employee employee = new Employee("Chris", null); return employee; }
2.3. Package level Null safety annotations.
Suppose, you want the entire package methods and variables to be not null, then it would require considerable effort to add @NonNull to every parameter and return type in the package. It could also lead to manual errors.
To avoid this, Spring introduced @NonNullApi and @NonNullFields to annotate the entire package as non-null.
- @NonNullApi – To annotate method parameters and return types as not null
- @NonNullFields – To annotate fields as not null
Okay. So how do we annotate the entire package as non-null? Create a package-info.java file in the intended package. Then, annotate the package as @NonNullApi or @NonNullFields based on your need as shown in the below images.
Here, we declared the entire package as not null. But in some scenarios. Few fields or methods can be null and shouldn’t be declared as Not null. In that case, use @Nullable annotation to declare the variable or method can be null.
3. Conclusion
In this article, we discussed the Null-safety of the Spring framework with examples. As mentioned, these annotations work as expected with IntelliJ IDEA and throw compile time errors.
See our other articles on Spring AOP.
This article is short, precise and to the point with good examples. Keep it up.