安全導航運算子
安全導航運算子(?.
)用於避免 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 Expression Language 支援對以下型別結構進行空安全索引訪問。
以下示例展示瞭如何將安全導航運算子用於列表索引(?.[]
)。
-
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 | 在可能為 null 的 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 | 在可能為 null 的 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 | 在可能為 null 的 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 | 在可能為 null 的 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 | 在可能為 null 的 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 | 在可能為 null 的 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 | 在複合表示式中使用“空安全選擇第一個”和空安全屬性訪問運算子。 |