本篇文章主要介紹以下幾個知識點:
- 關鍵字 lateinit
- 關鍵字 sealed class
內(nèi)容參考自第一行代碼第3版
1. 對變量延遲初始化:關鍵字 lateinit
Kotlin 語言的許多特性,如變量不可變,變量不可為空等都是為了盡可能保證程序安全而設定的,但有些時候這些特性在編碼時卻會帶來不少麻煩。
當你的類中存在很多全局變量實例听皿,為了滿足空指針檢查語法標準,不得不做很多的非空判斷,即使確定它們不會為空刊橘。
以項目中常見的 adapter 為例,在 activity 中的偽代碼如下:
class MainActivity : AppCompatActivity(), View.OnClickListener {
private var adapter: MyAdapter? = null // 把 adapter 設為全局變量
override fun onCreate(savedInstanceState: Bundle?) {
adapter = MyAdapter(mList) // 初始化 adapter
}
override fun onClick(v: View?) {
adapter?.notifyItemInserted(mList.size - 1) // 點擊調(diào)用 adapter 的方法
}
}
上述代碼中颂鸿,把 adapter 設置為全局變量促绵,在 onCreate()
中初始化,從而不得不先將 adapter 賦值為 null
嘴纺, 并把它類型聲明為 MyAdapter?
败晴,當調(diào)用 adapter 方法時還需進行判空處理(即使已經(jīng)初始化過了)。
代碼中有大量的全局變量時栽渴,就得編寫大量額外的判空處理尖坤,這時候就可以考慮對全局變量進行延遲初始化。
延遲初始化用的是 lateinit
關鍵字闲擦,它相當于告訴 Kotlin 編譯器會在晚些時候?qū)@個變量進行初始化慢味,這樣一開始就不用對它賦值 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ào)用 adapter 的方法纯路,此時無需做判空處理
}
}
當然或油,在用了 lateinit
關鍵字后,若變量還沒初始化的情況下就使用它感昼,則會拋出 UninitializedPropertyAccessException
異常装哆。
另外,還可以通過 isInitialized
來判斷一個全局變量是否已經(jīng)完成了初始化定嗓,這樣也能在某些時候避免重復對某個變量初始化操作:
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)化代碼:關鍵字 sealed class
首先來看一個例子蜕琴,這里定義一個 Result 接口,再分別定義成功類和失敗類去實現(xiàn)這個接口:
interface Result
class Success(val msg: String) : Result
class Failure(val error: Exception) : Result
接下來在定義一個方法用于獲取結(jié)果的信息:
fun getResultMsg(result: Result) = when (result) {
is Success -> result.msg
is Failure -> result.error.message
else -> throw IllegalArgumentException()
}
上述代碼存在的問題:
雖然只有兩種情況宵溅,但還是不得不再編寫個 else 條件來判斷凌简,否則編譯不通過。
倘若新增了一個 Unknow 類并實現(xiàn) Result 接口恃逻,但沒在
getResultMsg()
方法中添加相應的條件判斷雏搂,編譯器不會提醒,而是會走 else 條件語句寇损,從而拋出異常凸郑。
這時候就可以考慮用使用密封類優(yōu)化代碼。
密封類的關鍵字是 sealed class
矛市,當在 when 語句中傳入一個密封類變量作為條件時芙沥,編譯器會自動檢查該密封類有哪些子類,并強制要求將每一個子類對應的條件全部處理浊吏。
用 sealed class
而昨,上述代碼可改為:
sealed class Result
class Success(val msg: String) : Result()
class Failure(val error: Exception) : Result()
此時,getResultMsg()
方法中就無需編寫 else 條件了:
fun getResultMsg(result: Result) = when (result) {
is Success -> result.msg
is Failure -> result.error.message
}
注:密封類及其所有子類只能定義在同一個文件的頂層位置找田,不能嵌套在其他類中歌憨,這是被密封類底層的實現(xiàn)機制所限制的。
小結(jié):密封類可以使代碼更加嚴謹墩衙。
本篇文章就介紹到這务嫡。