Kotlin學(xué)習(xí) 3 -- 延遲初始化和密封類

本篇文章主要介紹以下幾個(gè)知識(shí)點(diǎn):

SUMMER DAY (圖片來源于網(wǎng)絡(luò))

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()
}

上述代碼存在的問題:

  1. 雖然只有兩種情況,但還是不得不再編寫個(gè) else 條件來判斷,否則編譯不通過。

  2. 倘若新增了一個(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)。

本篇文章就介紹到這。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容