安全导航运算符
安全导航运算符 (?.) 用于避免 NullPointerException,它来自
Groovy 语言。
通常,当你拥有对象的引用时,你可能需要在访问对象的方法或属性之前验证它是否不为 null。
为了避免这种情况,安全导航运算符针对特定的空安全操作返回 null,而不是抛出异常。
|
当安全导航运算符针对复合表达式中的特定空安全操作评估为 有关详细信息,请参阅 复合表达式中的空安全操作。 |
安全属性和方法访问
下例展示了如何使用安全导航运算符进行属性访问 (?.)。
-
Java
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);
| 1 | 在非空 placeOfBirth 属性上使用安全导航运算符 |
| 2 | 在空 placeOfBirth 属性上使用安全导航运算符 |
|
安全导航运算符也适用于对象上的方法调用。 例如,如果上下文中未配置 |
安全索引访问
Infra 表达式语言支持对以下类型的结构进行索引的安全导航。
下例展示了如何使用安全导航运算符对列表进行索引 (?.[])。
-
Java
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);
| 1 | 在非空 members 列表上使用空安全索引运算符 |
| 2 | 在空 members 列表上使用空安全索引运算符 |
安全集合选择和投影
-
空安全选择:
?.? -
空安全选择第一个:
?.^ -
空安全选择最后一个:
?.$ -
空安全投影:
?.!
下例展示了如何使用安全导航运算符进行集合选择 (?.?)。
-
Java
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);
| 1 | 在可能为空的 members 列表上使用空安全选择运算符 |
下例展示了如何使用集合的“空安全选择第一个”运算符 (?.^)。
-
Java
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);
| 1 | 在可能为空的 members 列表上使用“空安全选择第一个”运算符 |
下例展示了如何使用集合的“空安全选择最后一个”运算符 (?.$)。
-
Java
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);
| 1 | 在可能为空的 members 列表上使用“空安全选择最后一个”运算符 |
下例展示了如何使用安全导航运算符进行集合投影 (?.!)。
-
Java
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);
| 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
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);
| 1 | 在复合表达式中使用“空安全选择第一个”和空安全属性访问运算符。 |