Safe Navigation Operator

安全的导航操作符 (?.) 用于避免 NullPointerException,并且来自 Groovy 语言。通常,当你拥有指向对象的引用时,在访问该对象的属性或方法之前,你可能需要验证该对象未 null。为了避免这种情况,安全的导航操作符将返回特殊 null 安全操作的 null,而不是抛出异常。

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.

当安全导航运算符对于复合表达式中某个特定空安全操作评估为 null 时,该复合表达式的剩余部分仍将被评估。

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.

有关详细信息,请参阅 Null-safe Operations in Compound Expressions

Safe Property and Method Access

以下示例演示如何对属性访问使用安全导航运算符(?.)。

The following example shows how to use the safe navigation operator for property access (?.).

Java
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
Kotlin
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)
3 Use safe navigation operator on non-null placeOfBirth property
4 Use safe navigation operator on null placeOfBirth property

安全导航运算符也适用于对对象上的方法调用。

The safe navigation operator also applies to method invocations on an object.

例如,如果在上下文中未配置 #calculator 变量,表达式 #calculator?.max(4, 2) 将计算为 null。否则,将在 #calculator 上调用 max(int, int) 方法。

For example, the expression #calculator?.max(4, 2) evaluates to null if the #calculator variable has not been configured in the context. Otherwise, the max(int, int) method will be invoked on the #calculator.

Safe Index Access

自 Spring Framework 6.2 起,Spring 表达式语言支持对以下类型结构使用安全导航进行索引。

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
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
Kotlin
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)
3 Use null-safe index operator on a non-null members list
4 Use null-safe index operator on a null members list

Safe Collection Selection and Projection

Spring 表达式语言通过以下运算符支持集合选择和集合投影的安全导航。

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
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
Kotlin
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>
2 Use null-safe selection operator on potentially null members list

以下示例演示如何对集合使用“null 安全选择第一个”运算符(?.^)。

The following example shows how to use the "null-safe select first" operator for collections (?.^).

Java
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
Kotlin
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)
2 Use "null-safe select first" operator on potentially null members list

以下示例演示如何对集合使用“null 安全选择最后一个”运算符(?.$)。

The following example shows how to use the "null-safe select last" operator for collections (?.$).

Java
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
Kotlin
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)
2 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
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
Kotlin
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)
3 Use null-safe projection operator on non-null members list
4 Use null-safe projection operator on null members list

Null-safe Operations in Compound Expressions

如本部分开头所述,当安全导航运算符对复合表达式中的特定 null 安全操作计算为 null 时,复合表达式的其余部分仍将被计算。这意味着必须在整个复合表达式中应用安全导航运算符,以避免任何不必要的 NullPointerException

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.

给定表达式 #person?.address.city,如果 #personnull,则安全导航运算符(?.)确保在尝试访问 #personaddress 属性时不会引发异常。但是,由于 #person?.address 计算为 null,因此在尝试访问 nullcity 属性时将引发 NullPointerException。为了解决此问题,可以在复合表达式中应用 null 安全导航,如 #person?.address?.city。如果 #person#person?.address 计算为 null,则该表达式将安全地计算为 null

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.

以下示例演示了如何在复合表达式中将“null 安全选择第一个”运算符(?.^)用于一个集合,同时将 null 安全属性访问(?.)与之结合起来。如果 membersnull,则“null 安全选择第一个”运算符(members?.^[nationality == 'Serbian'])的结果将评估为 null,而安全导航运算符(?.name)的进一步使用可确保整个复合表达式评估为 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
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
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)
2 Use "null-safe select first" and null-safe property access operators within compound expression.