Groovy Goodness: Using NullCheck Annotation To Prevent NullPointerException
In Groovy we can apply the @NullCheck
annotation to a class, constructor or method. The annotation is an AST (Abstract Syntax Tree) transformation and will insert code that checks for null values in methods or constructors. If a null
value is passed to an annotated method or constructor, it will throw an IllegalArgumentException
. Without the annotation we could have a NullPointerException
if we try to invoke a method on the value we pass as argument. The annotation has an optional property includeGenerated
which by default is false
. If we set it to true
then the null checks are also applied to generated methods and constructors. This is very useful if we apply other AST transformations to our class that generates additional code.
In the following example we use the @NullCheck
annotation for a method and at class level:
import groovy.transform.NullCheck
@NullCheck
String upper(String value) {
"Upper:" + value.toUpperCase()
}
assert upper("groovy") == "Upper:GROOVY"
try {
upper(null)
} catch (IllegalArgumentException e) {
assert e.message == "value cannot be null"
}
import groovy.transform.NullCheck
// Apply null check for all constructors and methods.
@NullCheck
class Language {
private String name
Language(String name) {
this.name = name;
}
String upper(String prefix) {
return prefix + name.toUpperCase();
}
}
def groovy = new Language("groovy")
assert groovy.upper("Upper:") == "Upper:GROOVY"
// Method arguments are checked.
try {
groovy.upper(null)
} catch (IllegalArgumentException e) {
assert e.message == "prefix cannot be null"
}
// Constructor argument is also checked.
try {
def lang = new Language(null)
} catch (IllegalArgumentException e) {
assert e.message == "name cannot be null"
}
In the following example we set the includeGenerated
property to true to also generate null checks for generated code like the constructor generated by @TupleConstructor
:
import groovy.transform.NullCheck
import groovy.transform.TupleConstructor
@NullCheck(includeGenerated = true)
@TupleConstructor
class Language {
final String name
String upper(String prefix) {
return prefix + name.toUpperCase();
}
}
// Constructor is generated by @TupleConstructor and
// @NullCheck is applied to the generated constructor.
try {
def lang = new Language(null)
} catch (IllegalArgumentException e) {
assert e.message == "name cannot be null"
}
Written with Groovy 4.0.13