安全导航运算符
安全导航运算符 (?.
) 用于避免NullPointerException
,它来自Groovy 语言。通常,当您拥有对对象的引用时,您可能需要在访问对象的方法或属性之前验证它是否不为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"));
// 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 | 在非空placeOfBirth 属性上使用安全导航运算符 |
2 | 在空placeOfBirth 属性上使用安全导航运算符 |
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 | 在非空placeOfBirth 属性上使用安全导航运算符 |
2 | 在空placeOfBirth 属性上使用安全导航运算符 |
安全导航运算符也适用于对象上的方法调用。 例如,表达式 |
安全的索引访问
从 Spring Framework 6.2 开始,Spring 表达式语言支持对以下类型结构进行安全的索引导航。
以下示例演示如何将安全导航运算符用于列表索引 (?.[]
)。
-
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 | 在非空members 列表上使用空安全索引运算符 |
2 | 在空members 列表上使用空安全索引运算符 |
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 | 在非空members 列表上使用空安全索引运算符 |
2 | 在空members 列表上使用空安全索引运算符 |
安全的集合选择和投影
-
空安全选择:
?.?
-
空安全选择第一个:
?.^
-
空安全选择最后一个:
?.$
-
空安全投影:
?.!
以下示例演示如何将安全导航运算符用于集合选择 (?.?
)。
-
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 | 在可能为空的members 列表上使用空安全选择运算符 |
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 | 在可能为空的members 列表上使用空安全选择运算符 |
以下示例演示如何将集合的“空安全选择第一个”运算符 (?.^
) 用于集合。
-
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 | 在可能为空的members 列表上使用“空安全选择第一个”运算符 |
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 | 在可能为空的members 列表上使用“空安全选择第一个”运算符 |
以下示例演示如何将集合的“空安全选择最后一个”运算符 (?.$
) 用于集合。
-
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 | 在可能为空的members 列表上使用“空安全选择最后一个”运算符 |
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 | 在可能为空的members 列表上使用“空安全选择最后一个”运算符 |
以下示例演示如何将安全导航运算符用于集合投影 (?.!
)。
-
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 | 在非空members 列表上使用空安全投影运算符 |
2 | 在空members 列表上使用空安全投影运算符 |
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 | 在非空members 列表上使用空安全投影运算符 |
2 | 在空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)
// 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 | 在复合表达式中使用“空安全选择第一个”和空安全属性访问运算符。 |
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 | 在复合表达式中使用“空安全选择第一个”和空安全属性访问运算符。 |