1、延遲初始化
先看個(gè)實(shí)例看下延遲初始化的應(yīng)用場景:
private var messageAdapter: MessageListAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_message_list)
message_list.layoutManager = LinearLayoutManager(this)
initData()
messageAdapter = MessageListAdapter(this, data)
message_list.adapter = messageAdapter
btn_add.setOnClickListener {
messageAdapter?.notifyDataSetChanged()
}
}
我們聲明了成員變量messageAdapter,它是在onCreate()中進(jìn)行初始化的,所以不得不先將成員變量messageAdapter初始化為null,同時(shí)它的類型聲明成MessageListAdapter?,這也就導(dǎo)致了每次調(diào)用messageAdapter都需要進(jìn)行判空處理。如果我們類中定義很多成員變量,那么所有的變量在使用前都是需要進(jìn)行判空處理的。解決辦法也很簡單:就是對成員變量進(jìn)行延遲初始化,這樣就不需要在一開始就初始化為null了。
private lateinit var messageAdapter: MessageListAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_message_list)
message_list.layoutManager = LinearLayoutManager(this)
initData()
messageAdapter = MessageListAdapter(this, data)
message_list.adapter = messageAdapter
btn_add.setOnClickListener {
messageAdapter.notifyDataSetChanged()
Toast.makeText(this@MessageListActivity,"增加數(shù)據(jù)",Toast.LENGTH_SHORT).show()
}
}
this@MessageListActivity類似于Java中的MessageListActivity.this
可以看到我們在變量前加上了lateinit關(guān)鍵字,這樣就不用一開始初始化為null了,并且聲明類型為MessageListAdapter,由于這是不可空類型,那么我們在使用時(shí)就不需要進(jìn)行判空處理了。但是必須保證在任何地方調(diào)用時(shí),成員變量已經(jīng)初始化,否則會(huì)報(bào)UninitializedPropertyAccessException: lateinit property messageAdapter has not been initialized異常。
另外我們可以在初始化前進(jìn)行判斷,在未初始化時(shí)再進(jìn)行初始化,避免不必要的初始化。
if (!::messageAdapter.isInitialized)
messageAdapter = MessageListAdapter(this, data)
具體語法是::messageAdapter.isInitialized,這是固定寫法,用于判斷messageAdapter是否已經(jīng)初始化,如果沒有初始化則進(jìn)行初始化操作。
private lateinit var aa:Int
private lateinit var bb:Boolean
上面的聲明是會(huì)報(bào)錯(cuò)的
'lateinit' modifier is not allowed on properties of primitive types
注意:lateinit 不能修飾四類八種基礎(chǔ)類型int, double,boolean
2、使用密封類優(yōu)化代碼
先看下我們遇到的問題。
private fun getResultMsg(result: Result) = when (result) {
is Success -> result.msg
is Failure -> result.error.message
}
上面代碼是編譯不通過的,必須增加else分支
private fun getResultMsg(result: Result) = when (result) {
is Success -> result.msg
is Failure -> result.error.message
else ->"我是被逼的"
}
但是實(shí)際上Result只可能是Success 或Failure,所以不可能走到else分支,但是我們又必須增加else分支為了滿足Kotlin的語法標(biāo)準(zhǔn)。另外如果我們新增了一個(gè)Unknow類實(shí)現(xiàn)了Result了,用于表示執(zhí)行未知的結(jié)果,但是忘記在getResultMsg中增加邏輯分支,會(huì)直接走到了else分支,這不是我們想要的結(jié)果,而且編譯器是不會(huì)提醒你。
下面我們就看下使用密封類如何解決上面的問題
//密封類的定義
sealed class Result
class Success(val msg: String) : Result()
class Failure(val error: Exception) : Result()
//調(diào)用
private fun getResultMsg(result: Result) = when (result) {
is Success -> result.msg
is Failure -> result.error.message
}
密封類的定義也很簡單,使用sealed class來代替interface,這是已經(jīng)把Result定義成了密封類。
可以看到我們已經(jīng)不用增加無用的else分支了,這是為什么呢?
因?yàn)楫?dāng)在when語句中傳入一個(gè)密封類變量作為條件時(shí),Koltin編譯器會(huì)自動(dòng)檢查密封類有哪些子類,并強(qiáng)制要求每一個(gè)子類所對應(yīng)的條件全部處理。這樣就不會(huì)出現(xiàn)漏寫條件分支的情況了。
注意1:密封類及其子類只能定義在同一文件的頂層位置,不能嵌套在其他類中。
注意2:如果我們使用if來代替when進(jìn)行判斷,依然要增加else分支,密封類并不能解決問題。