本篇文章主要介紹以下幾個(gè)知識(shí)點(diǎn):
- 關(guān)鍵字 lateinit
- 關(guān)鍵字 sealed class
內(nèi)容參考自第一行代碼第3版

1. 對(duì)變量延遲初始化:關(guān)鍵字 lateinit
Kotlin 語(yǔ)言的許多特性,如變量不可變,變量不可為空等都是為了盡可能保證程序安全而設(shè)定的,但有些時(shí)候這些特性在編碼時(shí)卻會(huì)帶來不少麻煩。
當(dāng)你的類中存在很多全局變量實(shí)例,為了滿足空指針檢查語(yǔ)法標(biāo)準(zhǔn),不得不做很多的非空判斷,即使確定它們不會(huì)為空。
以項(xiàng)目中常見的 adapter 為例,在 activity 中的偽代碼如下:
class MainActivity : AppCompatActivity(), View.OnClickListener {
private var adapter: MyAdapter? = null // 把 adapter 設(shè)為全局變量
override fun onCreate(savedInstanceState: Bundle?) {
adapter = MyAdapter(mList) // 初始化 adapter
}
override fun onClick(v: View?) {
adapter?.notifyItemInserted(mList.size - 1) // 點(diǎn)擊調(diào)用 adapter 的方法
}
}
上述代碼中,把 adapter 設(shè)置為全局變量,在 onCreate() 中初始化,從而不得不先將 adapter 賦值為 null, 并把它類型聲明為 MyAdapter?,當(dāng)調(diào)用 adapter 方法時(shí)還需進(jìn)行判空處理(即使已經(jīng)初始化過了)。
代碼中有大量的全局變量時(shí),就得編寫大量額外的判空處理,這時(shí)候就可以考慮對(duì)全局變量進(jìn)行延遲初始化。
延遲初始化用的是 lateinit 關(guān)鍵字,它相當(dāng)于告訴 Kotlin 編譯器會(huì)在晚些時(shí)候?qū)@個(gè)變量進(jìn)行初始化,這樣一開始就不用對(duì)它賦值 null 了。
用 lateinit,上述代碼可改為:
class MainActivity : AppCompatActivity(), View.OnClickListener {
private lateinit var adapter: MyAdapter // 延遲初始化 adapter
override fun onCreate(savedInstanceState: Bundle?) {
adapter = MyAdapter(mList) // 初始化 adapter
}
override fun onClick(v: View?) {
adapter.notifyItemInserted(mList.size - 1) // 點(diǎn)擊調(diào)用 adapter 的方法,此時(shí)無(wú)需做判空處理
}
}
當(dāng)然,在用了 lateinit 關(guān)鍵字后,若變量還沒初始化的情況下就使用它,則會(huì)拋出 UninitializedPropertyAccessException 異常。
另外,還可以通過 isInitialized 來判斷一個(gè)全局變量是否已經(jīng)完成了初始化,這樣也能在某些時(shí)候避免重復(fù)對(duì)某個(gè)變量初始化操作:
class MainActivity : AppCompatActivity(), View.OnClickListener {
private lateinit var adapter: MyAdapter // 延遲初始化 adapter
override fun onCreate(savedInstanceState: Bundle?) {
// ::adapter.isInitialized 判斷 adapter 變量是否已經(jīng)初始化
if(!::adapter.isInitialized){
adapter = MyAdapter(mList) // 沒有初始化則初始化 adapter
}
}
}
2. 使用密封類優(yōu)化代碼:關(guān)鍵字 sealed class
首先來看一個(gè)例子,這里定義一個(gè) Result 接口,再分別定義成功類和失敗類去實(shí)現(xiàn)這個(gè)接口:
interface Result
class Success(val msg: String) : Result
class Failure(val error: Exception) : Result
接下來在定義一個(gè)方法用于獲取結(jié)果的信息:
fun getResultMsg(result: Result) = when (result) {
is Success -> result.msg
is Failure -> result.error.message
else -> throw IllegalArgumentException()
}
上述代碼存在的問題:
雖然只有兩種情況,但還是不得不再編寫個(gè) else 條件來判斷,否則編譯不通過。
倘若新增了一個(gè) Unknow 類并實(shí)現(xiàn) Result 接口,但沒在
getResultMsg()方法中添加相應(yīng)的條件判斷,編譯器不會(huì)提醒,而是會(huì)走 else 條件語(yǔ)句,從而拋出異常。
這時(shí)候就可以考慮用使用密封類優(yōu)化代碼。
密封類的關(guān)鍵字是 sealed class,當(dāng)在 when 語(yǔ)句中傳入一個(gè)密封類變量作為條件時(shí),編譯器會(huì)自動(dòng)檢查該密封類有哪些子類,并強(qiáng)制要求將每一個(gè)子類對(duì)應(yīng)的條件全部處理。
用 sealed class,上述代碼可改為:
sealed class Result
class Success(val msg: String) : Result()
class Failure(val error: Exception) : Result()
此時(shí),getResultMsg() 方法中就無(wú)需編寫 else 條件了:
fun getResultMsg(result: Result) = when (result) {
is Success -> result.msg
is Failure -> result.error.message
}
注:密封類及其所有子類只能定義在同一個(gè)文件的頂層位置,不能嵌套在其他類中,這是被密封類底層的實(shí)現(xiàn)機(jī)制所限制的。
小結(jié):密封類可以使代碼更加嚴(yán)謹(jǐn)。
本篇文章就介紹到這。