帶你初識Kotlin(三)

 

前言

用kotlin開發android應用,各種新鮮的語法糖層出不窮,真tm香,這篇博文主要記錄一些需要重點注意的地方。

記錄點

1.xxx調用了getXxx()/setXxx方法

toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
val actionBar = supportActionBar

在這裏supportActionBar其實時調用了AppCompatActivitygetSupportActionBar()方法。

val decorView = window.decorView
decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
window.statusBarColor = Color.TRANSPARENT

在這裏window其實時調用了ActivitygetWindow()方法。

2.Kotlin null safety相關 ? 和!!和 ?:

先闡述兩個概念:

“?”加在變量名後,系統在任何情況不會報它的空指針異常。

“!!”加在變量名後,如果對象爲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

3.let,with,run,apply,also函數區別

通過以上幾種函數的介紹,可以很方便優化kotlin中代碼編寫,整體看起來幾個函數的作用很相似,但是各自又存在着不同。使用的場景有相同的地方比如run函數就是let和with的結合體。下面一張表格可以清晰對比出他們的不同之處。

 

函數名 定義inline的結構 函數體內使用的對象 返回值 是否是擴展函數 適用的場景
let fun T.let(block: (T) -> R): R = block(this) it指代當前對象 閉包形式返回 適用於處理不爲null的操作場景
with fun with(receiver: T, block: T.() -> R): R = receiver.block() this指代當前對象或者省略 閉包形式返回 適用於調用同一個類的多個方法時,可以省去類名重複,直接調用類的方法即可,經常用於Android中RecyclerView中onBinderViewHolder中,數據model的屬性映射到UI上
run fun T.run(block: T.() -> R): R = block() this指代當前對象或者省略 閉包形式返回 適用於let,with函數任何場景。
apply fun T.apply(block: T.() -> Unit): T { block(); return this } this指代當前對象或者省略 返回this

1、適用於run函數的任何場景,一般用於初始化一個對象實例的時候,操作對象屬性,並最終返回這個對象。
2、動態inflate出一個XML的View的時候需要給View綁定數據也會用到.

3、一般可用於多個擴展函數鏈式調用
4、數據model多層級包裹判空處理的問題

 also fun T.also(block: (T) -> Unit): T { block(this); return this } | it指代當前對象   返回this 適用於let函數的任何場景,一般可用於多個擴展函數鏈式調用 

egg:

fun showProgressDialog(title: String?, message: String) {
    if (progressDialog == null) {
        progressDialog = ProgressDialog(this).apply {
            if (title != null) {
                setTitle(title)
            }
            setMessage(message)
            setCancelable(false)
        }
    }
    progressDialog?.show()
}
fun closeProgressDialog() {
    //表示object不爲null的條件下,纔會去執行let函數體
    progressDialog?.let {
        if (it.isShowing) {
            it.dismiss()
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章