網(wǎng)站首頁 編程語言 正文
對變量延遲初始化
Kotlin語言有許多特性,包括變量不可變,變量不可為空,等等。這些特性都是為了盡可能地保證程序安全而設(shè)計的,但是有些時候這些特性也會在編碼時給我們帶來不少麻煩。
比如,你的類中存在許多全局變量實例,為了保證他們能夠滿足kotlin的空指針檢查語法標(biāo)準(zhǔn),你不得不做出許多的非空判斷保護才行,即使你非常確定它們不會為空。
通過一個例子:
class MainActivity:AppCompatActivity(),View.OnClickListener{
private var adapter:MsgAdapter?=null
override fun onCreate(savedInstanceState:Bundle?){
...
adapter=MsgAdapter(msgList)
...
}
override fun onClick(v:View?){
...
adapter?.notifyItemInserted(msgList.size-1)
...
}
}
這里我們將adapter設(shè)置了全局變量,但是它的初始化工作是在onCreate()方法中進行的,因此不得不先將adapter賦值為null,同時把它的類型聲明成MsgAdapter?。
雖然我們會在onCreate()方法中對adapter進行初始化,同時能確保onClick()方法必然在onCreate()方法之后才會調(diào)用,但是我們在onClick()方法中調(diào)用adapter的任何方法時仍然要進行判空處理才行,否則編譯肯定無法通過。
而當(dāng)你的代碼中有了越來越多的全局變量實例時,這個問題就會變得越來越明顯,到時候你可能必須編寫大量額外的判空處理代碼,只是為了滿足kotlin編譯器的要求。
這個問題其實是可以解決的,而且非常簡單,那就是對全局變量進行延遲初始化。
延遲初始化使用的是lateinit關(guān)鍵字,它可以告訴kotlin編譯器,我會在晚些時候?qū)@個變量進行初始化,這樣我就不用在一開始的時候就將它賦值為null了。
接下來我就就使用延遲初始化的方式對上述代碼進行優(yōu)化,如下所示:
class MainActivity:AppCompatActivity(),View.OnClickListener{
private lateinit var adapter:MsgAdapter
override fun onCreate(savedInstanceState:Bundle?){
...
adapter=MsgAdapter(msgList)
...
}
override fun onClick(v:View?){
...
adapter.notifyItemInserted(msgList.size-1)
...
}
}
可以看到,我們在adapter變量的前面加上lateinit關(guān)鍵字,這樣就不用在一開始的時候?qū)⑺x值為null,同時類型聲明也就可以改成MsgAdapter了,由于MsgAdapter是不可為空的類型,所以我們在onClick()方法中也就不再需要進行判空處理,直接調(diào)用adaper的任何方法就可以了。
當(dāng)然,使用lateinit關(guān)鍵字也不是沒有任何風(fēng)險,如果在adapter變量還沒有初始化的情況下就直接使用它,那么程序就一定會奔潰,并且拋出一個UninitializedPropertyAccessException異常。
所以,當(dāng)你對一個全局變量使用了lateinit關(guān)鍵字時,請一定要確保它在被任何地方調(diào)用之前已經(jīng)完成了初始化工作,否則Kotlin將無法保證程序的安全性。
另外,我們還可以通過代碼來判斷一個全局變量是否已經(jīng)完成了初始化,這些某些時候能夠有效地避免對某一個變量進行初始化操作,示例代碼如下:
class MainActivity:AppCompatActivity(),View.onClickListener{
private lateinit var adapter:MsgAdapter
override fun onCreate(savedInstanceState:Bundle?){
...
if(!::adapter.isInitialized){
adapter=MsgAdapter(msgList)
}
...
}
}
::adapter.isInitialized可用于判斷adapter變量是否已經(jīng)初始化。然后我們再對結(jié)果進行取反,如果還沒有初始化,那就立即對adapter變量進行初始化,否則什么都不做。
使用密封類優(yōu)化代碼
密封類通常可以結(jié)合RecyclerView適配器中的ViewHolder一起使用,它可以在很多時候幫助你寫出更加規(guī)范和安全的代碼。
通過一個例子:
新建一個Kotlin文件
interface Result{
}
class Success(val msg:String):Result
class Failure(val error:Exception):Result
這里定義一個Result接口,用于表示某個操作的執(zhí)行結(jié)果,接口中不用編寫任何內(nèi)容,然后定義了兩個類去實現(xiàn)Result接口:一個Success類用于表示成功時的結(jié)果,一個Failure類用于表示失敗時的結(jié)果。
接下來定義一個getResultMsg()方法,用于獲取最終執(zhí)行結(jié)果的信息,代碼如下:
fun getResultMsg(result: Result)=when(result){
is Success-> result.msg
is Failure-> result.error
else -> throw IllegalArgumentException()
}
getResultMsg()方法中接收一個Result參數(shù),我們通過when語句來判斷:如果Result屬于Success,那么就返回成功的消息;如果Result屬于Failure,那么就返回錯誤信息。接下來我們不得不再編寫一個else條件,否則Kotlin編譯器會認為這里缺少條件分支,代碼將無法編譯通過。但實際上Result的執(zhí)行結(jié)果只可能是Success或Failure,這個else條件永遠走不到,所以我們在這里直接拋出異常,只是為了滿足kotlin編譯器的語法檢查而已。
但是else還有一個潛在風(fēng)險,如果我們現(xiàn)在新增一個Unkown類并實現(xiàn)Result接口,用于表示未知的執(zhí)行結(jié)果,但是如果沒有在getResultMsg()方法中添加相應(yīng)的條件分支,編譯器這種情況下不會提醒我們而是直接運行進入else條件里面。
這個時候密封類可以解決這個問題,密封類的關(guān)鍵字是sealed class,將Result接口改造成密封類的寫法:
sealed class Result{
}
class Success(val msg:String): Result()
class Failure(val error:Exception):Result()
這個時候會發(fā)現(xiàn)getResultMsg()方法中的else條件已經(jīng)不需要了,如下所示
fun getResultMsg(result: Result)=when(result){
is Success-> result.msg
is Failure-> result.error
}
這是因為當(dāng)在when語句中傳入一個密封類變量作為條件時,Kotlin編譯器會自動檢查該密封類有哪些子類,并強制要求你每一個子類所對應(yīng)的條件全部處理。這樣就可以保證,即使沒有編寫else條件,也不可能會出現(xiàn)漏寫條件分支的情況。而如果我們新增一個Unknown類,并也讓它繼承自Result,此時getResultMsg()方法就一定會報錯,必須新增一個Unknown的條件分支才能讓代碼編譯通過。
密封類及其所有子類只能定義在同一個文件的頂層位置,不能嵌套在其他類中,這是被密封類底層的實現(xiàn)機制所限制的。
接下來看一下它是如何結(jié)合MsgAdapter中的ViewHolder一起使用,并優(yōu)化一下MsgAdapter中的代碼。
比如在MsgAdapter中的onBindViewHolder()方法中存在一個沒有實際作用的else條件,只是拋出一個異常而已。對于這部分的代碼,我們就可以借助密封類的特性來進行優(yōu)化。新建一個MsgViewHolder.kt文件,其中加入如下代碼:
sealed class MsgViewHolder(view:View):RecyclerView.ViewHolder(view){
}
class LeftViewHolder(view: View):MsgViewHolder(view){
val leftMsg:TextView=view.findViewById(R.id.leftMsg)
}
class RightViewHolder(view: View):MsgViewHolder(view){
val rightMsg:TextView=view.findViewById(R.id.rightMsg)
}
這里我們定義了一個密封類MsgViewHolder,并讓他繼承自RecyclerView.ViewHolder,然后讓leftViewHolder和RightViewHolder繼承自MsgViewHolder。這樣就相當(dāng)于密封類MsgViewHolder只有兩個已知子類,因此在when語句中只要處理這兩種情況的條件分支即可。
修改MsgAdapter代碼,如下所示:
class MsgAdapter(val msgList:List<msg>):RecyclerView.Adapter<MsgViewHolder>(){
...
override fun onBindViewHolder(holder:MsgViewHolder,position:Int){
val msg=msgList[position]
when(holder){
is LeftViewHolder -> holder.leftMsg.text=msg.content
is RightViewHolder -> holder.rightMsg.text=msg.content
}
}
...
}
這里我們將RecycleView.Adapter的泛型指定成剛剛定義的密封類MsgViewHolder,這樣onBindViewHolder()方法傳入的參數(shù)就變成了MsgViewHolder。然后我們只要在when語句當(dāng)中處理LeftViewHolder和RightViewHolder這兩種情況就可以了,else也不需要了。這種RecyclerView適配器的寫法更加規(guī)范也更加推薦。
原文鏈接:https://blog.csdn.net/ChenYiRan123456/article/details/127797126
相關(guān)推薦
- 2022-06-06 CZGL.ProcessMetrics處理監(jiān)控數(shù)據(jù)的三種方式介紹_實用技巧
- 2022-06-28 python神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)使用Keras進行回歸運算_python
- 2022-02-25 commons-fileupload文件上傳的時候?qū)懭雸D片無數(shù)據(jù)
- 2022-06-27 Python使用re模塊實現(xiàn)okenizer(表達式分詞器)_python
- 2022-07-11 UVM中uvm_config_db在sequence中的使用
- 2022-12-10 Qt界面中滑動條的實現(xiàn)方式_C 語言
- 2023-08-01 elementui中Table切換分頁保存多選框選中的數(shù)據(jù)
- 2022-05-28 使用pandas計算環(huán)比和同比的方法實例_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支