Document safe navigation semantics within compound expressions in SpEL
Closes gh-21827
This commit is contained in:
parent
4a5dc7c1b0
commit
4a3ef3e24a
|
|
@ -5,7 +5,17 @@ The safe navigation operator (`?`) is used to avoid a `NullPointerException` and
|
|||
from the https://www.groovy-lang.org/operators.html#_safe_navigation_operator[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` instead of throwing an exception.
|
||||
this, the safe navigation operator returns `null` for the particular null-safe operation
|
||||
instead of throwing an exception.
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
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.
|
||||
|
||||
See <<expressions-operator-safe-navigation-compound-expressions>> for details.
|
||||
====
|
||||
|
||||
[[expressions-operator-safe-navigation-property-access]]
|
||||
== Safe Property and Method Access
|
||||
|
|
@ -283,3 +293,71 @@ Kotlin::
|
|||
======
|
||||
|
||||
|
||||
[[expressions-operator-safe-navigation-compound-expressions]]
|
||||
== 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.
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
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.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
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.
|
||||
======
|
||||
|
|
|
|||
|
|
@ -689,6 +689,25 @@ class SpelDocumentationTests extends AbstractExpressionTests {
|
|||
assertThat(inventor).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeSelectFirstAndPropertyAccess() {
|
||||
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);
|
||||
assertThat(name).isEqualTo("Nikola Tesla");
|
||||
|
||||
society.members = null;
|
||||
|
||||
// evaluates to null - does not throw a NullPointerException
|
||||
name = parser.parseExpression(expression)
|
||||
.getValue(context, String.class);
|
||||
assertThat(name).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void nullSafeSelectLast() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue