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
- 解決方法1是把var變數改為val變數
val mutableString:String = "a"
fun run() {
printText(mutableString)
}
- 解決方法2是寫一個新的val變數,將var變數賦值給它,將val作為引數
fun run() {
mutableString = "a"
val string = mutableString ?: ""
printText (string)
}
使用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
使用的也是非空型別,使用起來更方便了。原始碼在此下載