Null Safety (空值安全)
在 Kotlin 中,可以擁有一個 null
值。當某些東西遺失或尚未設定時,Kotlin 會使用 null
值。
你已經在 Collections (集合) 章節中看過 Kotlin 傳回 null
值的範例,
當時你嘗試使用不存在於 map (映射) 中的 key (鍵) 來存取 key-value pair (鍵值對)。雖然以這種方式使用
null
值很有用,但如果你的程式碼沒有準備好處理它們,你可能會遇到問題。
為了幫助預防程式中 null
值所造成的問題,Kotlin 具備 null safety (Null 安全) 機制。Null safety (Null 安全) 會在編譯時期而非執行時期偵測到 null
值的潛在問題。
Null safety (Null 安全) 是多種功能的組合,可讓你:
- 明確宣告何時允許在你的程式中使用
null
值。 - 檢查
null
值。 - 使用安全呼叫 (safe call) 來呼叫可能包含
null
值的屬性或函式。 - 宣告偵測到
null
值時要採取的動作。
Nullable types (可為 Null 的類型)
Kotlin 支援 nullable types (可為 Null 的類型),允許宣告的類型具有 null
值。預設情況下,類型
不允許接受 null
值。Nullable types (可為 Null 的類型) 通過在類型宣告後明確添加 ?
來宣告。
例如:
fun main() {
// neverNull 具有 String 類型
var neverNull: String = "This can't be null"
// 拋出編譯器錯誤
neverNull = null
// nullable 具有可為 Null 的 String 類型
var nullable: String? = "You can keep a null here"
// 這是 OK 的
nullable = null
// 預設情況下,不接受 null 值
var inferredNonNull = "The compiler assumes non-nullable"
// 拋出編譯器錯誤
inferredNonNull = null
// notNull 不接受 null 值
fun strLength(notNull: String): Int {
return notNull.length
}
println(strLength(neverNull)) // 18
println(strLength(nullable)) // 拋出編譯器錯誤
}
length
是 String 類別的一個屬性,
其中包含字串中的字元數。
Check for null values (檢查 Null 值)
你可以在條件運算式中檢查 null
值是否存在。在以下範例中,describeString()
函式有一個 if
語句,用於檢查 maybeString
是否 不是 null
且其 length
是否大於零:
fun describeString(maybeString: String?): String {
if (maybeString != null && maybeString.length > 0) {
return "String of length ${maybeString.length}"
} else {
return "Empty or null string"
}
}
fun main() {
val nullString: String? = null
println(describeString(nullString))
// Empty or null string
}
Use safe calls (使用安全呼叫)
若要安全地存取可能包含 null
值的物件的屬性,請使用安全呼叫 (safe call) 運算子 ?.
。如果物件或其存取的屬性之一為 null
,則安全呼叫 (safe call)
運算子會傳回 null
。如果你想避免 null
值觸發程式碼中的錯誤,這非常有用。
在以下範例中,lengthString()
函式使用安全呼叫 (safe call) 來傳回字串的長度或 null
:
fun lengthString(maybeString: String?): Int? = maybeString?.length
fun main() {
val nullString: String? = null
println(lengthString(nullString))
// null
}
可以鏈式使用安全呼叫 (safe call),以便物件的任何屬性包含 null
值時,都會傳回 null
,而不會
拋出錯誤。例如:
person.company?.address?.country
安全呼叫 (safe call) 運算子也可以用於安全地呼叫 extension (擴展) 或 member function (成員函式)。在這種情況下,會在呼叫函式之前執行 null 檢查。如果檢查偵測到 null
值,則會跳過呼叫並傳回 null
。
在以下範例中,nullString
為 null
,因此會跳過 .uppercase()
的調用,並傳回 null
:
fun main() {
val nullString: String? = null
println(nullString?.uppercase())
// null
}
Use Elvis operator (使用 Elvis 運算子)
如果使用 Elvis 運算子 ?:
偵測到 null
值,你可以提供要傳回的預設值。
在 Elvis 運算子的左側寫入應檢查 null
值的内容。
在 Elvis 運算子的右側寫入如果偵測到 null
值應傳回的内容。
在以下範例中,nullString
為 null
,因此存取 length
屬性的安全呼叫 (safe call) 會傳回 null
值。
因此,Elvis 運算子會傳回 0
:
fun main() {
val nullString: String? = null
println(nullString?.length ?: 0)
// 0
}
如需有關 Kotlin 中 null safety (Null 安全) 的更多資訊,請參閱 Null safety (Null 安全)。
Practice (練習)
Exercise (練習)
你具有 employeeById
函式,可讓你存取公司的員工資料庫。不幸的是,此
函式傳回 Employee?
類型的值,因此結果可能為 null
。你的目標是編寫一個函式,該函式在提供員工 id
時傳回員工的薪資,如果員工在資料庫中遺失,則傳回 0
。
|---|---|
data class Employee (val name: String, var salary: Int)
fun employeeById(id: Int) = when(id) {
1 `->` Employee("Mary", 20)
2 `->` null
3 `->` Employee("John", 21)
4 `->` Employee("Ann", 23)
else `->` null
}
fun salaryById(id: Int) = // Write your code here
fun main() {
println((1..5).sumOf { id `->` salaryById(id) })
}
|---|---|
data class Employee (val name: String, var salary: Int)
fun employeeById(id: Int) = when(id) {
1 `->` Employee("Mary", 20)
2 `->` null
3 `->` Employee("John", 21)
4 `->` Employee("Ann", 23)
else `->` null
}
fun salaryById(id: Int) = employeeById(id)?.salary ?: 0
fun main() {
println((1..5).sumOf { id `->` salaryById(id) })
}
What's next? (下一步是什麼?)
恭喜!既然你已經完成了 Kotlin 導覽,請查看我們關於熱門 Kotlin 應用程式的教學課程: