Kotlin如何避免“!!”(非空斷言)

當我們把Java自動轉成Kotlin的時候,代碼裏會出現很多非空斷言!!。或者某些場景下因爲IDE提示或編譯錯誤,也讓我們自己加上了一些!!
但使用!!的後果是有可能拋出IllegalArgumentException:Parameter specified as non-null is null

如何避免!!

使用?.let/?.apply/?.run

這種是最常用的方法,也是首選的方法。但當有多個變量同時要判空時,或者需要處理爲null時的邏輯,這種方式稍微有一點麻煩,下面會講到一些新的方式。

disposable?.let {
    if (!it.isDisposed) it.dispose()
}

用Val替代Var

var mutableString:String? = null

fun run() {
    mutableString = "a"
    printText(mutableString)
}

fun printText(text: String) {
    ...
}

此時會報錯`Smart cast to 'String' is impossible, because 'multableString' is a mutable property that could have been changed by this time
:app:compileDebugKotlin FAILED`。由於multableString是Var變量,爲了避免多線程對變量的修改而出現Null的情況,kotlin從編譯上進行了限制。

  • 解決方法1是把var變量改爲val變量
val mutableString:String = "a"

fun run() {
    printText(mutableString)
}
  • 解決方法2是寫一個新的val變量,將var變量賦值給它,將val作爲參數
fun run() {
    mutableString = "a"
    val string = mutableString ?: ""
    printText(string)
}

<!-- more -->

使用Elvis操作符

fun run() {
    multableString = "a"
    printText(multableString ?: "")
}

聲明lateinit

使用lateinit聲明到變量上,表示這個變量延遲初始化,比較適合在Activity.onCreate這種有生命週期的方法裏初始化。

lateinit var mutableString: String
override fun onCreate(savedInstanceState: Bundle?) {
    multableString = "a"
    printText(mutableString)
}
需要注意的是,訪問未初始化的 lateinit 修飾的屬性會拋出UninitializedPropertyAccessException異常

注意:基本類型是不能使用lateinit的。會拋錯'lateinit' modifier is not allowed on properties of primitive types

lateinit var mutableInt: Int

代理屬性

如果需要對基本類型等做非空處理,可以使用代理屬性。

var mutableInt: Int by Delegates.notNull<Int>()
override fun onCreate(savedInstanceState: Bundle?) {
    mutableInt = 1
}

一定要在初始化賦值之後才能讀取mutableInt,不然會拋IllegalStateException:Property ${property.name} should be initialized before get.

空與非空處理

val result = multableString.notNullElse {
    "$it is not null"
} ({ "is null" })

新開發的方法notNullElse,對單個變量判空處理,非空時傳入it爲非空類型,提高了便捷性,爲空時使用第二個block來返回值。適合那裏需要判空,返回值result也是非空的類型,比較實用。源碼在此下載

多個值非空

private var mLinearLayout: LinearLayout? = null

...
private fun initView(context: Context) {
    mLinearLayout = LinearLayout(context)
}

...

if (tvItem == null) {
    mLinearLayout!!.addView(childTvItem)
} else {
    mLinearLayout!!.addView(childTvItem, mLinearLayout!!.indexOfChild(tvItem) + 1)
}

當我們要對多個值判斷的時候,let就不那麼好用了,但如果不使用let就拿不到非空的類型,像上面要判斷2個都不爲空時做操作,爲空時另外一個邏輯。其實一早我們就已經判斷空了,有沒有更好的方法呢?

allNotNullElse(tvItem, mLinearLayout) { a, b ->
    b.addView(childTvItem, b.indexOfChild(tvItem) + 1)
} ({ mLinearLayout?.addView(childTvItem) })

新開發的方法allNotNullElse返回的a, b 兩個值已經是非空類型了,這樣addView使用的也是非空類型,使用起來更方便了。源碼在此下載

喜歡這篇文章,請點贊收藏,謝謝!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章