1. 程式人生 > >Kotlin中?和!!的區別

Kotlin中?和!!的區別

很多同學剛上手使用Kotlin知道它有針對Java NullPointerException的管理,而在Kotlin中?和!!均是和NullPointerException有關係,可他們的區別到底是什麼呢?為什麼別人開發的專案中出現了好多"?",而我讀起來卻滿臉問號。

   
不懂就問百度呀,確實有人解釋它們的區別,比如:
   
這是輸入 "kotlin ?和!!"
搜尋到的百度第一條答案,確實這位說的沒錯。不過我覺得對於一個剛接觸KT(Kotlin)的新手來說,他恐怕需要有漢語八級才能透徹理解這兩句話的意思。

先闡述兩個概念:
"?"加在變數名後,系統在任何情況不會報它的空指標異常。
"!!"加在變數名後,如果物件為null,那麼系統一定會報異常!

 

先拿Java程式碼舉個例子

        ArrayList<String> myList = null;     //  建立一個null的佇列
        Log.d("TAG", "-->> List Size = " + myList.size()); 

這個例子中,執行到Log列印佇列長度時,大家都知道系統一定會報NullPointerException。然而如果在KT中,在呼叫myList的時候在它後面加上一個問號myList?.size(),當myList為null的時候直接會列印List Size = null並不會有null異常出現。

當使用Android Studio把上面那段Java自動轉換成KT程式碼寫法後:

        val myList : ArrayList<String>? = null
        Log.d("TAG", "-->> List Size = ${myList!!.size}")

編譯器為什麼自動把myList.size()變成了myList!!.size呢,為什麼加上的是感嘆號不是問號。

這是因為編譯器在轉化時為了保證程式碼轉化前後的一致性所造成的。換句話說,在Java上出異常的,轉化到KT上,編譯器任然會讓他保持丟擲異常,NullPointerException也是如此。

所以結合上下文可以看得出,!!加上去後好像並沒有和之前Java程式碼有什麼區別嘛,該null的地方任然會丟擲異常。所以大多數情況下都會使用?來檢測null,輪不到!!出場。!!只會在你需要對某物件進行非空判斷,並且需要丟擲異常時才會使用到。

那我們接下來著重講解一下?到底怎麼用。
在宣告物件時,把它跟在類名後面,表示這個類允許為null;
在呼叫物件時,把它跟在物件後面,表示如果為null程式就會視而不見。
如下列程式碼:

    // 這是宣告一個變數,問號跟在類名後面
    var room: Room? = Room()

    private fun checkRoom() { // 因為加上了問號,所以可以任意的把room變成空 room = null // 因為在呼叫時加上了問號,所以程式不會丟擲異常 Log.d("TAG", "-->> room name = ${room?.roomName}") } 

再舉個不用?的例子:

    // 這樣程式就預設的給room加上了!!,從此以後room不允許為null
    var room: Room = Room()

    private fun checkRoom() { // 當把null賦給room時,從編譯的時候就已經不通過 room = null // 並且編譯器建議把物件後面的問號刪除,因為這個物件永遠不為空 Log.d("TAG", "-->> room name = ${room.roomName}") } 

所以加上?是一種安全的寫法,它體現了Kotlin null safety的特性。
KT的語法很靈動,定義引數還可以寫成

    val room: Room? = Room()    // 先例項化一個room,並且room可以為空
    val room: Room? = null  // 不例項化了,開始room就是空的

    val room: Room = Room()   // 例項化一個room,並且room永遠不能為空
    val room = Room()   // 和上一行程式碼一樣,是KT最常用的簡寫語法 



然而加上問號以後程式就萬事大吉永遠擺脫了NullPointerException的煩惱?我們再看下一段程式碼:

        val roomList: ArrayList<Room>? = null
        if (roomList?.size > 0) { Log.d("TAG", "-->> 房間數不是0") } 

當我們判斷list.size的時候,編譯器會告訴我們"Operator call corresponds to a dot-qualified call 'roomList?.size.compareTo(0)' which is not allowed on a nullable receiver 'roomList?.size'."。大概意思是,當roomList為null的時,它的size返回就是"null",但是"null"不可以和int值比大小,所以編譯器建議我們寫成roomList?.size!! > 0

沒錯,經過編譯器的建議加上了!!,我們程式執行到這行程式碼,roomList為null時它一定會報異常。所以是不是必須得在外面套一層if(roomList != null)這種Java常見語句才能避免異常嗎?

當然Kotlin不會讓程式出現這種囉嗦的程式碼,所以裡面提供了物件A ?: 物件B表示式,並且取消了Java中的條件表示式 ? 表示式1 : 表示式2這個三元表示式。

?:表示的意思是,當物件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", "-->> 房間數不是0") } 

就目前為止使,用上面的??:基本上能避免程式中出現的所有NullPointerException。



作者:Jason_Samuel
連結:https://www.jianshu.com/p/51b2e5aa3dd8
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。