網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
前言
屬性或?qū)ο蟮难訒r(shí)加載是我們相當(dāng)常用的,一般我們都是使用 lateinit 和 by lazy 來(lái)實(shí)現(xiàn)。
他們兩者都是延時(shí)初始化,那么在使用時(shí)那么他們兩者有什么區(qū)別呢?
lateinit
見(jiàn)名知意,延時(shí)初始化的標(biāo)記。lateinit var可以讓我們聲明一個(gè)變量并且不用馬上初始化,在我們需要的時(shí)候進(jìn)行手動(dòng)初始化即可。
如果我們不初始化會(huì)怎樣?
private lateinit var name: String
findViewById<Button>(R.id.btn_load).click {
YYLogUtils.w("name:$name age:$age")
}
會(huì)報(bào)錯(cuò):
所以對(duì)應(yīng)這一種情況我們會(huì)有一個(gè)是否初始化的判斷
private lateinit var name: String
findViewById<Button>(R.id.btn_load).click {
if (this::name.isInitialized) {
YYLogUtils.w("name:$name age:$age")
}
}
lateinit var的作用相對(duì)較簡(jiǎn)單,其實(shí)就是讓編譯期在檢查時(shí)不要因?yàn)閷傩宰兞课幢怀跏蓟鴪?bào)錯(cuò)。(注意一定要記得初始化哦!)
by lazy
by lazy 委托延時(shí)處理,分為委托和延時(shí)
其實(shí)如果我們不想延時(shí)初始化,我們直接使用委托by也可以實(shí)現(xiàn)。
private var age: Int by Delegates.observable(18) { property, oldValue, newValue ->
YYLogUtils.w("發(fā)生了回調(diào) property:$property oldValue:$oldValue newValue:$newValue")
}
findViewById<Button>(R.id.btn_load).click {
age = 25
YYLogUtils.w("name:$name age:$age")
}
我們通過(guò) by Delegates 的方式就可以指定委托對(duì)象,這里我用的 Delegates.obsevable 它的作用是修改 age 的值之后會(huì)有回調(diào)的處理。
運(yùn)行的效果:
除了 Delegates.obsevable 它還有其他的用法。
public object Delegates {
public fun <T : Any> notNull(): ReadWriteProperty<Any?, T> = NotNullVar()
public inline fun <T> observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit):
ReadWriteProperty<Any?, T> =
object : ObservableProperty<T>(initialValue) {
override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(property, oldValue, newValue)
}
public inline fun <T> vetoable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Boolean):
ReadWriteProperty<Any?, T> =
object : ObservableProperty<T>(initialValue) {
override fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = onChange(property, oldValue, newValue)
}
}
private class NotNullVar<T : Any>() : ReadWriteProperty<Any?, T> {
private var value: T? = null
public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.")
}
public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = value
}
}
- notNull方法我們可以看到就是說(shuō)這個(gè)對(duì)象不能為null,否則就會(huì)拋出異常。
- observable方法主要用于監(jiān)控屬性值發(fā)生變更,類似于一個(gè)觀察者。當(dāng)屬性值被修改后會(huì)往外部拋出一個(gè)變更的回調(diào)。
- vetoable方法跟observable類似,都是用于監(jiān)控屬性值發(fā)生變更,當(dāng)屬性值被修改后會(huì)往外部拋出一個(gè)變更的回調(diào)。與observable不同的是這個(gè)回調(diào)會(huì)返回一個(gè)Boolean值,來(lái)決定此次屬性值是否執(zhí)行修改。
其實(shí)用不用委托沒(méi)什么區(qū)別,就是看是否需要屬性變化的回調(diào)監(jiān)聽(tīng),否則我們直接用變量即可
private var age: Int = 18
findViewById<Button>(R.id.btn_load).click {
age = 25
YYLogUtils.w("name:$name age:$age")
}
如果我們想實(shí)現(xiàn)延時(shí)初始化的關(guān)鍵就是 lazy 關(guān)鍵字,所以,lazy是如何工作的呢? 讓我們一起在Kotlin標(biāo)準(zhǔn)庫(kù)參考中總結(jié)lazy()方法,如下所示:
- lazy() 返回的是一個(gè)存儲(chǔ)在lambda初始化器中的Lazy類型實(shí)例。
- getter的第一次調(diào)用執(zhí)行傳遞給lazy()的lambda并存儲(chǔ)其結(jié)果。
- 后面再調(diào)用的話,getter調(diào)用只返回存儲(chǔ)中的值。
簡(jiǎn)單地說(shuō),lazy創(chuàng)建一個(gè)實(shí)例,在第一次訪問(wèn)屬性值時(shí)執(zhí)行初始化,存儲(chǔ)結(jié)果并返回存儲(chǔ)的值。
private val age: Int by lazy { 18 / 2 }
findViewById<Button>(R.id.btn_load).click {
age = 25
YYLogUtils.w("name:$name age:$age")
}
由于我們使用的是 by lazy ,歸根到底還是一種委托,只是它是一種特殊的委托,它的過(guò)程是這樣的:
我們的屬性 age 需要 by lazy 時(shí),它生成一個(gè)該屬性的附加屬性:age?delegate。 在構(gòu)造器中,將使用 lazy(()->T) 創(chuàng)建的 Lazy 實(shí)例對(duì)象賦值給 age?delegate。 當(dāng)該屬性被調(diào)用,即其getter方法被調(diào)用時(shí)返回 age?delegate.getVaule(),而 age?delegate.getVaule()方法的返回結(jié)果是對(duì)象 age?delegate 內(nèi)部的 _value 屬性值,在getVaule()第一次被調(diào)用時(shí)會(huì)將_value進(jìn)行初始化并儲(chǔ)存起來(lái),往后都是直接將_value的值返回,從而實(shí)現(xiàn)屬性值的唯一一次的初始化,并無(wú)法再次修改。所以它是只讀的。
當(dāng)我們調(diào)用這個(gè) age 這個(gè)屬性的時(shí)候才會(huì)初始化,它屬于一種懶加載,既然是懶加載,就必然涉及到線程安全的問(wèn)題,我們看看lazy是怎么解決的。
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
when (mode) {
LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
}
public actual fun <T> lazy(lock: Any?, initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer, lock)
我們需要考慮的是線程安全和非線程安全
- SYNCHRONIZED通過(guò)加鎖來(lái)確保只有一個(gè)線程可以初始化Lazy實(shí)例,是線程安全的
- PUBLICATION表示不加鎖,可以并發(fā)訪問(wèn)多次調(diào)用,但是我之接收第一個(gè)返回的值作為L(zhǎng)azy的實(shí)例,其他后面返回的是啥玩意兒我不管。這也是線程安全的
- NONE不加鎖,是線程不安全的
總結(jié)
總的來(lái)說(shuō)其實(shí) lateinit 是延遲初始化, by lazy 是懶加載即初始化方式已確定,只是在使用的時(shí)候執(zhí)行。
雖然兩者都可以推遲屬性初始化的時(shí)間,但是 lateinit var 只是讓編譯期忽略對(duì)屬性未初始化的檢查,后續(xù)在哪里以及何時(shí)初始化還需要開(kāi)發(fā)者自己決定。而by lazy真正做到了聲明的同時(shí)也指定了延遲初始化時(shí)的行為,在屬性被第一次被使用的時(shí)候能自動(dòng)初始化。
并且 lateinit 是可讀寫的,by lazy 是只讀的。
那我們什么時(shí)候該使用 lateinit,什么時(shí)候使用 by lazy ?
其實(shí)大部分情況下都可以通用,只是 by lazy 一般用于非空只讀屬性,需要延遲加載情況,而 lateinit 一般用于非空可變屬性,需要延遲加載情況。
原文鏈接:https://juejin.cn/post/7152689103794864159
相關(guān)推薦
- 2022-06-24 python包相關(guān)知識(shí)點(diǎn)之包的導(dǎo)入、相對(duì)路徑以及絕對(duì)路徑_python
- 2022-03-29 redis的list數(shù)據(jù)類型相關(guān)命令介紹及使用_Redis
- 2022-01-28 Hyper集成laravel中使用的blade模板
- 2022-06-12 GO語(yǔ)言中常見(jiàn)的排序算法使用示例_Golang
- 2022-06-18 C語(yǔ)言?深入講解條件編譯的用處_C 語(yǔ)言
- 2022-04-20 詳解C語(yǔ)言讀取文件求某一列的平均值_C 語(yǔ)言
- 2023-06-21 ProtoBuf動(dòng)態(tài)拆分Gradle?Module解析_Android
- 2022-06-30 Python?pickle模塊實(shí)現(xiàn)Python對(duì)象持久化存儲(chǔ)_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支