Safe Navigation Operator
The safe navigation operator (?.
) is used to avoid a NullPointerException
and comes
from the Groovy
language. Typically, when you have a reference to an object, you might need to verify
that it is not null
before accessing methods or properties of the object. To avoid
this, the safe navigation operator returns null
for the particular null-safe operation
instead of throwing an exception.
When the safe navigation operator evaluates to See Null-safe Operations in Compound Expressions for details. |
Safe Property and Method Access
The following example shows how to use the safe navigation operator for property access
(?.
).
-
Java
-
Kotlin
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));
// evaluates to "Smiljan"
String city = parser.parseExpression("placeOfBirth?.city") (1)
.getValue(context, tesla, String.class);
tesla.setPlaceOfBirth(null);
// evaluates to null - does not throw NullPointerException
city = parser.parseExpression("placeOfBirth?.city") (2)
.getValue(context, tesla, String.class);
1 | Use safe navigation operator on non-null placeOfBirth property |
2 | Use safe navigation operator on null placeOfBirth property |
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
val tesla = Inventor("Nikola Tesla", "Serbian")
tesla.setPlaceOfBirth(PlaceOfBirth("Smiljan"))
// evaluates to "Smiljan"
var city = parser.parseExpression("placeOfBirth?.city") (1)
.getValue(context, tesla, String::class.java)
tesla.setPlaceOfBirth(null)
// evaluates to null - does not throw NullPointerException
city = parser.parseExpression("placeOfBirth?.city") (2)
.getValue(context, tesla, String::class.java)
1 | Use safe navigation operator on non-null placeOfBirth property |
2 | Use safe navigation operator on null placeOfBirth property |
The safe navigation operator also applies to method invocations on an object. For example, the expression |
Safe Index Access
Since Spring Framework 6.2, the Spring Expression Language supports safe navigation for indexing into the following types of structures.
The following example shows how to use the safe navigation operator for indexing into
a list (?.[]
).
-
Java
-
Kotlin
ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
EvaluationContext context = new StandardEvaluationContext(society);
// evaluates to Inventor("Nikola Tesla")
Inventor inventor = parser.parseExpression("members?.[0]") (1)
.getValue(context, Inventor.class);
society.members = null;
// evaluates to null - does not throw an exception
inventor = parser.parseExpression("members?.[0]") (2)
.getValue(context, Inventor.class);
1 | Use null-safe index operator on a non-null members list |
2 | Use null-safe index operator on a null members list |
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
// evaluates to Inventor("Nikola Tesla")
var inventor = parser.parseExpression("members?.[0]") (1)
.getValue(context, Inventor::class.java)
society.members = null
// evaluates to null - does not throw an exception
inventor = parser.parseExpression("members?.[0]") (2)
.getValue(context, Inventor::class.java)
1 | Use null-safe index operator on a non-null members list |
2 | Use null-safe index operator on a null members list |
Safe Collection Selection and Projection
The Spring Expression Language supports safe navigation for collection selection and collection projection via the following operators.
-
null-safe selection:
?.?
-
null-safe select first:
?.^
-
null-safe select last:
?.$
-
null-safe projection:
?.!
The following example shows how to use the safe navigation operator for collection
selection (?.?
).
-
Java
-
Kotlin
ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
StandardEvaluationContext context = new StandardEvaluationContext(society);
String expression = "members?.?[nationality == 'Serbian']"; (1)
// evaluates to [Inventor("Nikola Tesla")]
List<Inventor> list = (List<Inventor>) parser.parseExpression(expression)
.getValue(context);
society.members = null;
// evaluates to null - does not throw a NullPointerException
list = (List<Inventor>) parser.parseExpression(expression)
.getValue(context);
1 | Use null-safe selection operator on potentially null members list |
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
val expression = "members?.?[nationality == 'Serbian']" (1)
// evaluates to [Inventor("Nikola Tesla")]
var list = parser.parseExpression(expression)
.getValue(context) as List<Inventor>
society.members = null
// evaluates to null - does not throw a NullPointerException
list = parser.parseExpression(expression)
.getValue(context) as List<Inventor>
1 | Use null-safe selection operator on potentially null members list |
The following example shows how to use the "null-safe select first" operator for
collections (?.^
).
-
Java
-
Kotlin
ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
StandardEvaluationContext context = new StandardEvaluationContext(society);
String expression =
"members?.^[nationality == 'Serbian' || nationality == 'Idvor']"; (1)
// evaluates to Inventor("Nikola Tesla")
Inventor inventor = parser.parseExpression(expression)
.getValue(context, Inventor.class);
society.members = null;
// evaluates to null - does not throw a NullPointerException
inventor = parser.parseExpression(expression)
.getValue(context, Inventor.class);
1 | Use "null-safe select first" operator on potentially null members list |
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
val expression =
"members?.^[nationality == 'Serbian' || nationality == 'Idvor']" (1)
// evaluates to Inventor("Nikola Tesla")
var inventor = parser.parseExpression(expression)
.getValue(context, Inventor::class.java)
society.members = null
// evaluates to null - does not throw a NullPointerException
inventor = parser.parseExpression(expression)
.getValue(context, Inventor::class.java)
1 | Use "null-safe select first" operator on potentially null members list |
The following example shows how to use the "null-safe select last" operator for
collections (?.$
).
-
Java
-
Kotlin
ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
StandardEvaluationContext context = new StandardEvaluationContext(society);
String expression =
"members?.$[nationality == 'Serbian' || nationality == 'Idvor']"; (1)
// evaluates to Inventor("Pupin")
Inventor inventor = parser.parseExpression(expression)
.getValue(context, Inventor.class);
society.members = null;
// evaluates to null - does not throw a NullPointerException
inventor = parser.parseExpression(expression)
.getValue(context, Inventor.class);
1 | Use "null-safe select last" operator on potentially null members list |
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
val expression =
"members?.$[nationality == 'Serbian' || nationality == 'Idvor']" (1)
// evaluates to Inventor("Pupin")
var inventor = parser.parseExpression(expression)
.getValue(context, Inventor::class.java)
society.members = null
// evaluates to null - does not throw a NullPointerException
inventor = parser.parseExpression(expression)
.getValue(context, Inventor::class.java)
1 | Use "null-safe select last" operator on potentially null members list |
The following example shows how to use the safe navigation operator for collection
projection (?.!
).
-
Java
-
Kotlin
ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
StandardEvaluationContext context = new StandardEvaluationContext(society);
// evaluates to ["Smiljan", "Idvor"]
List placesOfBirth = parser.parseExpression("members?.![placeOfBirth.city]") (1)
.getValue(context, List.class);
society.members = null;
// evaluates to null - does not throw a NullPointerException
placesOfBirth = parser.parseExpression("members?.![placeOfBirth.city]") (2)
.getValue(context, List.class);
1 | Use null-safe projection operator on non-null members list |
2 | Use null-safe projection operator on null members list |
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
// evaluates to ["Smiljan", "Idvor"]
var placesOfBirth = parser.parseExpression("members?.![placeOfBirth.city]") (1)
.getValue(context, List::class.java)
society.members = null
// evaluates to null - does not throw a NullPointerException
placesOfBirth = parser.parseExpression("members?.![placeOfBirth.city]") (2)
.getValue(context, List::class.java)
1 | Use null-safe projection operator on non-null members list |
2 | Use null-safe projection operator on null members list |
Null-safe Operations in Compound Expressions
As mentioned at the beginning of this section, when the safe navigation operator
evaluates to null
for a particular null-safe operation within a compound expression,
the remainder of the compound expression will still be evaluated. This means that the
safe navigation operator must be applied throughout a compound expression in order to
avoid any unwanted NullPointerException
.
Given the expression #person?.address.city
, if #person
is null
the safe navigation
operator (?.
) ensures that no exception will be thrown when attempting to access the
address
property of #person
. However, since #person?.address
evaluates to null
, a
NullPointerException
will be thrown when attempting to access the city
property of
null
. To address that, you can apply null-safe navigation throughout the compound
expression as in #person?.address?.city
. That expression will safely evaluate to null
if either #person
or #person?.address
evaluates to null
.
The following example demonstrates how to use the "null-safe select first" operator
(?.^
) on a collection combined with null-safe property access (?.
) within a compound
expression. If members
is null
, the result of the "null-safe select first" operator
(members?.^[nationality == 'Serbian']
) evaluates to null
, and the additional use of
the safe navigation operator (?.name
) ensures that the entire compound expression
evaluates to null
instead of throwing an exception.
-
Java
-
Kotlin
ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
StandardEvaluationContext context = new StandardEvaluationContext(society);
String expression = "members?.^[nationality == 'Serbian']?.name"; (1)
// evaluates to "Nikola Tesla"
String name = parser.parseExpression(expression)
.getValue(context, String.class);
society.members = null;
// evaluates to null - does not throw a NullPointerException
name = parser.parseExpression(expression)
.getValue(context, String.class);
1 | Use "null-safe select first" and null-safe property access operators within compound expression. |
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
val expression = "members?.^[nationality == 'Serbian']?.name" (1)
// evaluates to "Nikola Tesla"
String name = parser.parseExpression(expression)
.getValue(context, String::class.java)
society.members = null
// evaluates to null - does not throw a NullPointerException
name = parser.parseExpression(expression)
.getValue(context, String::class.java)
1 | Use "null-safe select first" and null-safe property access operators within compound expression. |