可空類型與非空類型
在 Kotlin 中纷铣,類型區(qū)分引用可以為 null (可空引用),或不能為 null(非空引用)贡避。
- String 類型的常規(guī)變量不能容納 null
var a: String = "abc" // 默認(rèn)情況下痛黎,常規(guī)初始化意味著非空
a = null // 編譯錯誤
- 如果需要允許為空,我們可以聲明一個變量為可空字符串刮吧,寫作
String?:
var b: String? = "abc" // 可以設(shè)置為空
b = null // ok
print(b)
- 現(xiàn)在舅逸,如果你調(diào)用
a
的方法或者訪問它的屬性,它保證不會導(dǎo)致NPE
皇筛,這樣你就可以放心地使用:
val l = a.length
- 但是如果你想訪問
b
的同一個屬性,那么這是不安全的坠七,并且編譯器會報告一個錯誤:
val l = b.length // 錯誤:變量 b 可能為空
但是如果我們還是要進(jìn)行屬性訪問怎么辦? 請繼續(xù)往下看
在條件中檢測 null
- 顯式檢測 b 是否為 null
val l = if (b != null) b.length else -1
- 支持更復(fù)雜(更智能)的條件水醋,但需要注意的是,僅適用于
b
是不可變的情況(即在檢測和使用之間沒有修改過的局部變量 彪置,或者不可覆蓋并且有幕后字段的val
成員)拄踪,因為否則可能會發(fā)生在檢測之后b
又變?yōu)?null
的情況。
val b: String? = "Kotlin"
if (b != null && b.length > 0) {
print("String of length ${b.length}")
} else {
print("Empty string")
}
安全的調(diào)用
安全調(diào)用操作符:
?.
val a = "Kotlin"
val b: String? = null
println(b?.length)
println(a?.length) // 無需安全調(diào)用
如果 b
非空拳魁,就返回 b.length
惶桐,否則返回 null
,這個表達(dá)式的類型是 Int?
潘懊。
- 安全調(diào)用在鏈?zhǔn)秸{(diào)用中很有用姚糊。
例如,如果一個員工Bob
可能會(或者不會)分配給一個部門授舟, 并且可能有另外一個員工是該部門的負(fù)責(zé)人救恨,那么獲取Bob
所在部門負(fù)責(zé)人(如果有的話)的名字可以這樣寫:
bob?.department?.head?.name
如果任意一個屬性(環(huán)節(jié))為空,這個鏈?zhǔn)秸{(diào)用就會返回 null
释树。
- 如果要只對非空值執(zhí)行某個操作肠槽,安全調(diào)用操作符可以與
let
一起使用:
val listWithNulls: List<String?> = listOf("Kotlin", null)
for (item in listWithNulls) {
item?.let { println(it) } // 輸出 Kotlin 并忽略 null
}
- 安全調(diào)用也可以出現(xiàn)在賦值的左側(cè)。
這樣奢啥,如果調(diào)用鏈中的任何一個接收者為空都會跳過賦值秸仙,而右側(cè)的表達(dá)式根本不會求值:
// 如果 `person` 或者 `person.department` 其中之一為空,都不會調(diào)用該函數(shù):
person?.department?.head = managersPool.getManager()
Elvis 操作符
- Elvis 操作符:
?:
val l = b?.length ?: -1
如果 ?:
左側(cè)表達(dá)式非空桩盲,elvis
操作符就返回其左側(cè)表達(dá)式寂纪,否則返回右側(cè)表達(dá)式(僅當(dāng)左側(cè)為空時,才會對右側(cè)表達(dá)式求值)赌结。
- 對于
throw
和return
弊攘,我們可以這么寫:
fun foo(node: Node): String? {
val parent = node.getParent() ?: return null
val name = node.getName() ?: throw IllegalArgumentException("name expected")
// ……
}
非空斷言運(yùn)算符:!!
將任何值轉(zhuǎn)換為非空類型抢腐,若該值為空則拋出異常。
- 我們可以寫
b!!
襟交,這會返回一個非空的b
值 迈倍, 但如果 b 為空,就會拋出一個NPE
異常:
val l = b!!.length
因此捣域,如果你想要一個 NPE
啼染,你可以得到它,但是你必須顯式要求它焕梅,否則它不會不期而至迹鹅。
安全的類型轉(zhuǎn)換
使用安全的類型轉(zhuǎn)換,如果嘗試轉(zhuǎn)換不成功則返回
null
:
al aInt: Int? = a as? Int
可空類型的集合
- 如果你有一個可空類型元素的集合贞言,并且想要過濾非空元素斜棚,你可以使用 filterNotNull 來實現(xiàn):
val nullableList: List<Int?> = listOf(1, 2, null, 4)
val intList: List<Int> = nullableList.filterNotNull()