很多同學(xué)剛上手使用Kotlin知道它有針對Java NullPointerException的管理榨为,而在Kotlin中?和!!均是和NullPointerException有關(guān)系葵腹,可他們的區(qū)別到底是什么呢掺冠?為什么別人開發(fā)的項目中出現(xiàn)了好多"?",而我讀起來卻滿臉問號阶女。
不懂就問百度呀确镊,確實有人解釋它們的區(qū)別嗅剖,比如:
這是輸入"kotlin ?和!!"搜索到的百度第一條答案,確實這位說的沒錯矾利。不過我覺得對于一個剛接觸KT(Kotlin)的新手來說姑裂,他恐怕需要有漢語八級才能透徹理解這兩句話的意思。
先闡述兩個概念:
"?"加在變量名后男旗,系統(tǒng)在任何情況不會報它的空指針異常舶斧。
"!!"加在變量名后,如果對象為null察皇,那么系統(tǒng)一定會報異常茴厉!
先拿Java代碼舉個例子
ArrayList<String> myList = null; // 創(chuàng)建一個null的隊列
Log.d("TAG", "-->> List Size = " + myList.size());
這個例子中,執(zhí)行到Log打印隊列長度時让网,大家都知道系統(tǒng)一定會報NullPointerException呀忧。然而如果在KT中,在調(diào)用myList的時候在它后面加上一個問號myList?.size()
溃睹,當(dāng)myList為null的時候直接會打印List Size = null
并不會有null異常出現(xiàn)而账。
當(dāng)使用Android Studio把上面那段Java自動轉(zhuǎn)換成KT代碼寫法后:
val myList : ArrayList<String>? = null
Log.d("TAG", "-->> List Size = ${myList!!.size}")
編譯器為什么自動把myList.size()
變成了myList!!.size
呢,為什么加上的是感嘆號不是問號因篇。
這是因為編譯器在轉(zhuǎn)化時為了保證代碼轉(zhuǎn)化前后的一致性所造成的泞辐。換句話說,在Java上出異常的竞滓,轉(zhuǎn)化到KT上咐吼,編譯器任然會讓他保持拋出異常,NullPointerException也是如此商佑。
所以結(jié)合上下文可以看得出锯茄,!!加上去后好像并沒有和之前Java代碼有什么區(qū)別嘛,該null的地方任然會拋出異常茶没。所以大多數(shù)情況下都會使用?來檢測null肌幽,輪不到!!出場。!!只會在你需要對某對象進(jìn)行非空判斷抓半,并且需要拋出異常時才會使用到喂急。
那我們接下來著重講解一下?到底怎么用。
在聲明對象時笛求,把它跟在類名后面廊移,表示這個類允許為null糕簿;
在調(diào)用對象時,把它跟在對象后面狡孔,表示如果為null程序就會視而不見懂诗。
如下列代碼:
// 這是聲明一個變量,問號跟在類名后面
var room: Room? = Room()
private fun checkRoom() {
// 因為加上了問號步氏,所以可以任意的把room變成空
room = null
// 因為在調(diào)用時加上了問號响禽,所以程序不會拋出異常
Log.d("TAG", "-->> room name = ${room?.roomName}")
}
再舉個不用?的例子:
// 這樣程序就默認(rèn)的給room加上了!!,從此以后room不允許為null
var room: Room = Room()
private fun checkRoom() {
// 當(dāng)把null賦給room時荚醒,從編譯的時候就已經(jīng)不通過
room = null
// 并且編譯器建議把對象后面的問號刪除芋类,因為這個對象永遠(yuǎn)不為空
Log.d("TAG", "-->> room name = ${room.roomName}")
}
所以加上?是一種安全的寫法,它體現(xiàn)了Kotlin null safety的特性界阁。
KT的語法很靈動侯繁,定義參數(shù)還可以寫成
val room: Room? = Room() // 先實例化一個room,并且room可以為空
val room: Room? = null // 不實例化了泡躯,開始room就是空的
val room: Room = Room() // 實例化一個room贮竟,并且room永遠(yuǎn)不能為空
val room = Room() // 和上一行代碼一樣,是KT最常用的簡寫語法
然而加上問號以后程序就萬事大吉永遠(yuǎn)擺脫了NullPointerException的煩惱较剃?我們再看下一段代碼:
val roomList: ArrayList<Room>? = null
if (roomList?.size > 0) {
Log.d("TAG", "-->> 房間數(shù)不是0")
}
當(dāng)我們判斷l(xiāng)ist.size的時候咕别,編譯器會告訴我們"Operator call corresponds to a dot-qualified call 'roomList?.size.compareTo(0)' which is not allowed on a nullable receiver 'roomList?.size'."。大概意思是写穴,當(dāng)roomList為null的時惰拱,它的size返回就是"null",但是"null"不可以和int值比大小啊送,所以編譯器建議我們寫成roomList?.size!! > 0
偿短。
沒錯,經(jīng)過編譯器的建議加上了!!馋没,我們程序運行到這行代碼昔逗,roomList為null時它一定會報異常。所以是不是必須得在外面套一層if(roomList != null)
這種Java常見語句才能避免異常嗎篷朵?
當(dāng)然Kotlin不會讓程序出現(xiàn)這種啰嗦的代碼勾怒,所以里面提供了對象A ?: 對象B
表達(dá)式,并且取消了Java中的條件表達(dá)式 ? 表達(dá)式1 : 表達(dá)式2
這個三元表達(dá)式声旺。
?:表示的意思是控硼,當(dāng)對象A值為null的時候,那么它就會返回后面的對象B艾少。
val roomList: ArrayList<Room>? = null
val mySize= roomList?.size ?: 0
此時mySize的值就為0,因為roomList?.size為空翼悴。
所以我們可以把上面的代碼改成這樣:
val roomList: ArrayList<Room>? = null
if (roomList?.size ?: 0 > 0) { // 這一行添加了?:
Log.d("TAG", "-->> 房間數(shù)不是0")
}
就目前為止使缚够,用上面的?和?:基本上能避免程序中出現(xiàn)的所有NullPointerException幔妨。