安全导航操作符
安全导航操作符(?.
)用于避免 NullPointerException
,它源自 Groovy 语言。通常情况下,当你引用一个对象时,可能需要在访问该对象的方法或属性之前验证它是否为 null
。为了避免这种情况,安全导航操作符会在特定的空安全操作中返回 null
,而不是抛出异常。
当安全导航操作符在复合表达式中对某个空安全操作求值为 null
时,复合表达式的其余部分仍会被求值。
详情请参阅 复合表达式中的空安全操作。
安全属性和方法访问
以下示例展示了如何使用安全导航操作符进行属性访问(?.
)。
- Java
- Kotlin
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));
// 评估为 "Smiljan"
String city = parser.parseExpression("placeOfBirth?.city") 1
.getValue(context, tesla, String.class);
tesla.setPlaceOfBirth(null);
// 评估为 null - 不会抛出 NullPointerException
city = parser.parseExpression("placeOfBirth?.city") 2
.getValue(context, tesla, String.class);
在非空的
placeOfBirth
属性上使用安全导航操作符在空的
placeOfBirth
属性上使用安全导航操作符
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
val tesla = Inventor("Nikola Tesla", "Serbian")
tesla.setPlaceOfBirth(PlaceOfBirth("Smiljan"))
// 评估为 "Smiljan"
var city = parser.parseExpression("placeOfBirth?.city") 1
.getValue(context, tesla, String::class.java)
tesla.setPlaceOfBirth(null)
// 评估为 null - 不会抛出 NullPointerException
city = parser.parseExpression("placeOfBirth?.city") 2
.getValue(context, tesla, String::class.java)
在非空的
placeOfBirth
属性上使用安全导航操作符在空的
placeOfBirth
属性上使用安全导航操作符
安全导航操作符同样适用于对象上的方法调用。
例如,表达式 #calculator?.max(4, 2)
如果 #calculator
变量在上下文中未配置,则计算结果为 null
。否则,将在 #calculator
上调用 max(int, int)
方法。
安全索引访问
自 Spring Framework 6.2 起,Spring 表达式语言支持对以下类型的结构进行索引的安全导航。
以下示例展示了如何使用安全导航操作符对列表进行索引(?.[]
)。
- Java
- Kotlin
ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
EvaluationContext context = new StandardEvaluationContext(society);
// 评估为 Inventor("Nikola Tesla")
Inventor inventor = parser.parseExpression("members?.[0]") 1
.getValue(context, Inventor.class);
society.members = null;
// 评估为 null - 不会抛出异常
inventor = parser.parseExpression("members?.[0]") 2
.getValue(context, Inventor.class);
在非空的
members
列表上使用空安全索引操作符在空的
members
列表上使用空安全索引操作符
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
// 评估为 Inventor("Nikola Tesla")
var inventor = parser.parseExpression("members?.[0]") 1
.getValue(context, Inventor::class.java)
society.members = null
// 评估为 null - 不会抛出异常
inventor = parser.parseExpression("members?.[0]") 2
.getValue(context, Inventor::class.java)
在非空的
members
列表上使用空安全索引操作符在空的
members
列表上使用空安全索引操作符
安全集合选择与投影
-
空安全选择:
?.?
-
空安全选择第一个:
?.^
-
空安全选择最后一个:
?.$
-
空安全投影:
?.!
以下示例展示了如何在集合选择中使用安全导航操作符(?.?
)。
- Java
- Kotlin
ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
StandardEvaluationContext context = new StandardEvaluationContext(society);
String expression = "members?.?[nationality == 'Serbian']"; 1
// 评估结果为 [Inventor("Nikola Tesla")]
List<Inventor> list = (List<Inventor>) parser.parseExpression(expression)
.getValue(context);
society.members = null;
// 评估结果为 null - 不会抛出 NullPointerException
list = (List<Inventor>) parser.parseExpression(expression)
.getValue(context);
在可能为空的
members
列表上使用 null-safe 选择操作符
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
val expression = "members?.?[nationality == 'Serbian']" 1
// 评估结果为 [Inventor("Nikola Tesla")]
var list = parser.parseExpression(expression)
.getValue(context) as List<Inventor>
society.members = null
// 评估结果为 null - 不会抛出 NullPointerException
list = parser.parseExpression(expression)
.getValue(context) as List<Inventor>
在可能为空的
members
列表上使用 null-safe 选择操作符
以下示例展示了如何对集合使用“空安全选择第一个”操作符(?.^
)。
- Java
- Kotlin
ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
StandardEvaluationContext context = new StandardEvaluationContext(society);
String expression =
"members?.^[nationality == 'Serbian' || nationality == 'Idvor']"; 1
// 评估为 Inventor("Nikola Tesla")
Inventor inventor = parser.parseExpression(expression)
.getValue(context, Inventor.class);
society.members = null;
// 评估为 null - 不会抛出 NullPointerException
inventor = parser.parseExpression(expression)
.getValue(context, Inventor.class);
在可能为 null 的
members
列表上使用 "null-safe select first" 操作符
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
val expression =
"members?.^[nationality == 'Serbian' || nationality == 'Idvor']" 1
// 评估为 Inventor("Nikola Tesla")
var inventor = parser.parseExpression(expression)
.getValue(context, Inventor::class.java)
society.members = null
// 评估为 null - 不会抛出 NullPointerException
inventor = parser.parseExpression(expression)
.getValue(context, Inventor::class.java)
在可能为 null 的
members
列表上使用 "null-safe select first" 操作符
以下示例展示了如何在集合中使用“空安全选择最后一个”操作符(?.$
)。
- Java
- Kotlin
ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
StandardEvaluationContext context = new StandardEvaluationContext(society);
String expression =
"members?.$[nationality == 'Serbian' || nationality == 'Idvor']"; 1
// 求值为 Inventor("Pupin")
Inventor inventor = parser.parseExpression(expression)
.getValue(context, Inventor.class);
society.members = null;
// 求值为 null - 不会抛出 NullPointerException
inventor = parser.parseExpression(expression)
.getValue(context, Inventor.class);
在可能为 null 的
members
列表上使用 "null-safe select last" 操作符
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
val expression =
"members?.$[nationality == 'Serbian' || nationality == 'Idvor']" 1
// 求值为 Inventor("Pupin")
var inventor = parser.parseExpression(expression)
.getValue(context, Inventor::class.java)
society.members = null
// 求值为 null - 不会抛出 NullPointerException
inventor = parser.parseExpression(expression)
.getValue(context, Inventor::class.java)
在可能为 null 的
members
列表上使用 "null-safe select last" 操作符
以下示例展示了如何在集合投影中使用安全导航操作符(?.!
)。
- Java
- Kotlin
ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
StandardEvaluationContext context = new StandardEvaluationContext(society);
// 计算结果为 ["Smiljan", "Idvor"]
List placesOfBirth = parser.parseExpression("members?.![placeOfBirth.city]") 1
.getValue(context, List.class);
society.members = null;
// 计算结果为 null - 不会抛出 NullPointerException
placesOfBirth = parser.parseExpression("members?.![placeOfBirth.city]") 2
.getValue(context, List.class);
在非空的
members
列表上使用空安全投影操作符在空的
members
列表上使用空安全投影操作符
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
// 计算结果为 ["Smiljan", "Idvor"]
var placesOfBirth = parser.parseExpression("members?.![placeOfBirth.city]") 1
.getValue(context, List::class.java)
society.members = null
// 计算结果为 null - 不会抛出 NullPointerException
placesOfBirth = parser.parseExpression("members?.![placeOfBirth.city]") 2
.getValue(context, List::class.java)
在非空的
members
列表上使用空安全投影操作符在空的
members
列表上使用空安全投影操作符
复合表达式中的空安全操作
正如本节开头所提到的,当安全导航操作符在复合表达式中的某个特定空安全操作上求值为 null
时,复合表达式的其余部分仍会被求值。这意味着必须在整个复合表达式中应用安全导航操作符,以避免任何不必要的 NullPointerException
。
给定表达式 #person?.address.city
,如果 #person
为 null
,安全导航操作符(?.
)确保在尝试访问 #person
的 address
属性时不会抛出异常。然而,由于 #person?.address
的值为 null
,在尝试访问 null
的 city
属性时,将会抛出 NullPointerException
。为了解决这个问题,你可以在整个复合表达式中应用空安全导航,如 #person?.address?.city
。如果 #person
或 #person?.address
的值为 null
,该表达式将安全地求值为 null
。
以下示例展示了如何在复合表达式中结合使用集合上的“空安全选择第一个”操作符(?.^
)和空安全属性访问(?.
)。如果 members
为 null
,则“空安全选择第一个”操作符(members?.^[nationality == 'Serbian']
)的结果评估为 null
,并且额外使用安全导航操作符(?.name
)确保整个复合表达式评估为 null
,而不是抛出异常。
- Java
- Kotlin
ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
StandardEvaluationContext context = new StandardEvaluationContext(society);
String expression = "members?.^[nationality == 'Serbian']?.name"; 1
// 计算结果为 "Nikola Tesla"
String name = parser.parseExpression(expression)
.getValue(context, String.class);
society.members = null;
// 计算结果为 null - 不会抛出 NullPointerException
name = parser.parseExpression(expression)
.getValue(context, String.class);
在复合表达式中使用“空安全选择第一个”和空安全属性访问操作符。
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
val expression = "members?.^[nationality == 'Serbian']?.name" 1
// 计算结果为 "Nikola Tesla"
String name = parser.parseExpression(expression)
.getValue(context, String::class.java)
society.members = null
// 计算结果为 null - 不会抛出 NullPointerException
name = parser.parseExpression(expression)
.getValue(context, String::class.java)
在复合表达式中使用“空安全选择第一个”和空安全属性访问操作符。