广告位联系
返回顶部
分享到

Kotlin对象的懒加载方式by lazy与lateinit异同介绍

Android 来源:互联网 作者:佚名 发布时间:2022-10-11 21:22:21 人浏览
摘要

属性或对象的延时加载是我们相当常用的,一般我们都是使用 lateinit 和 by lazy 来实现。 他们两者都是延时初始化,那么在使用时那么他们两者有什么区别呢? lateinit 见名知意,延时初

属性或对象的延时加载是我们相当常用的,一般我们都是使用 lateinit 和 by lazy 来实现。

他们两者都是延时初始化,那么在使用时那么他们两者有什么区别呢?

lateinit

见名知意,延时初始化的标记。lateinit var可以让我们声明一个变量并且不用马上初始化,在我们需要的时候进行手动初始化即可。

如果我们不初始化会怎样?

1

2

3

4

private lateinit var name: String

findViewById<Button>(R.id.btn_load).click {

    YYLogUtils.w("name:$name age:$age")

}

会报错:

所以对应这一种情况我们会有一个是否初始化的判断

1

2

3

4

5

6

private lateinit var name: String

findViewById<Button>(R.id.btn_load).click {

    if (this::name.isInitialized) {

       YYLogUtils.w("name:$name age:$age")

    }  

}

lateinit var的作用相对较简单,其实就是让编译期在检查时不要因为属性变量未被初始化而报错。(注意一定要记得初始化哦!)

by lazy

by lazy 委托延时处理,分为委托和延时

其实如果我们不想延时初始化,我们直接使用委托by也可以实现。

1

2

3

4

5

6

7

private var age: Int by Delegates.observable(18) { property, oldValue, newValue ->

     YYLogUtils.w("发生了回调 property:$property oldValue:$oldValue newValue:$newValue")

 }

 findViewById<Button>(R.id.btn_load).click {

     age = 25

     YYLogUtils.w("name:$name age:$age")

 }

我们通过 by Delegates 的方式就可以指定委托对象,这里我用的 Delegates.obsevable 它的作用是修改 age 的值之后会有回调的处理。

运行的效果:

除了 Delegates.obsevable 它还有其他的用法。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

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方法我们可以看到就是说这个对象不能为null,否则就会抛出异常。
  • observable方法主要用于监控属性值发生变更,类似于一个观察者。当属性值被修改后会往外部抛出一个变更的回调。
  • vetoable方法跟observable类似,都是用于监控属性值发生变更,当属性值被修改后会往外部抛出一个变更的回调。与observable不同的是这个回调会返回一个Boolean值,来决定此次属性值是否执行修改。

其实用不用委托没什么区别,就是看是否需要属性变化的回调监听,否则我们直接用变量即可

1

2

3

4

5

private var age: Int  = 18

 findViewById<Button>(R.id.btn_load).click {

     age = 25

     YYLogUtils.w("name:$name age:$age")

 }

如果我们想实现延时初始化的关键就是 lazy 关键字,所以,lazy是如何工作的呢? 让我们一起在Kotlin标准库参考中总结lazy()方法,如下所示:

  • lazy() 返回的是一个存储在lambda初始化器中的Lazy类型实例。
  • getter的第一次调用执行传递给lazy()的lambda并存储其结果。
  • 后面再调用的话,getter调用只返回存储中的值。

简单地说,lazy创建一个实例,在第一次访问属性值时执行初始化,存储结果并返回存储的值。

1

2

3

4

5

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 ,归根到底还是一种委托,只是它是一种特殊的委托,它的过程是这样的:

我们的属性 age 需要 by lazy 时,它生成一个该属性的附加属性:age?delegate。 在构造器中,将使用 lazy(()->T) 创建的 Lazy 实例对象赋值给 age?delegate。 当该属性被调用,即其getter方法被调用时返回 age?delegate.getVaule(),而 age?delegate.getVaule()方法的返回结果是对象 age?delegate 内部的 _value 属性值,在getVaule()第一次被调用时会将_value进行初始化并储存起来,往后都是直接将_value的值返回,从而实现属性值的唯一一次的初始化,并无法再次修改。所以它是只读的。

当我们调用这个 age 这个属性的时候才会初始化,它属于一种懒加载,既然是懒加载,就必然涉及到线程安全的问题,我们看看lazy是怎么解决的。

1

2

3

4

5

6

7

8

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通过加锁来确保只有一个线程可以初始化Lazy实例,是线程安全的
  • PUBLICATION表示不加锁,可以并发访问多次调用,但是我之接收第一个返回的值作为Lazy的实例,其他后面返回的是啥玩意儿我不管。这也是线程安全的
  • NONE不加锁,是线程不安全的

总结

总的来说其实 lateinit 是延迟初始化, by lazy 是懒加载即初始化方式已确定,只是在使用的时候执行。

虽然两者都可以推迟属性初始化的时间,但是 lateinit var 只是让编译期忽略对属性未初始化的检查,后续在哪里以及何时初始化还需要开发者自己决定。而by lazy真正做到了声明的同时也指定了延迟初始化时的行为,在属性被第一次被使用的时候能自动初始化。

并且 lateinit 是可读写的,by lazy 是只读的。

那我们什么时候该使用 lateinit,什么时候使用 by lazy ?

其实大部分情况下都可以通用,只是 by lazy 一般用于非空只读属性,需要延迟加载情况,而 lateinit 一般用于非空可变属性,需要延迟加载情况。


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 : https://juejin.cn/post/7152689103794864159
相关文章
  • Kotlin的Collection与Sequence操作异同点介绍

    Kotlin的Collection与Sequence操作异同点介绍
    在Android开发中,集合是我们必备的容器,Kotlin的标准库中提供了很多处理集合的方法,而且还提供了两种基于容器的工作方式:Collection 和
  • 实现一个Kotlin函数类型方法

    实现一个Kotlin函数类型方法
    接口与函数类型 业务开发中,经常会有实现一个函数式接口(即接口只有一个方法需要实现)的场景,大家应该都会不假思索的写出如下代
  • Android10 App启动Activity源码分析
    ActivityThread的main方法 让我们把目光聚焦到ActivityThread的main方法上。 ActivityThread的源码路径为/frameworks/base/core/java/android/app/ActivityThread。 1 2
  • Android10客户端事务管理ClientLifecycleManager源码解析

    Android10客户端事务管理ClientLifecycleManager源码解析
    在Android 10 App启动分析之Activity启动篇(二)一文中,简单地介绍了Activity的生命周期管理器是如何调度Activity进入onCreate生命周期的流程。这
  • Kotlin对象的懒加载方式by lazy与lateinit异同介绍

    Kotlin对象的懒加载方式by lazy与lateinit异同介绍
    属性或对象的延时加载是我们相当常用的,一般我们都是使用 lateinit 和 by lazy 来实现。 他们两者都是延时初始化,那么在使用时那么他们两
  • Android类加载流程分析

    Android类加载流程分析
    本文分析的代码基于Android8.1.0源码。 流程分析 从loadClass开始,我们来看下Android中类加载的流程 /libcore/ojluni/src/main/java/java/lang/ClassLoader.ja
  • Android实现读写USB串口数据的代码

    Android实现读写USB串口数据的代码
    最近在研究USB方面的内容;先后做了关于Android读写HID、串口设备的DEMO。本文比较简单,主要介绍的是Android实现读取串口数据的功能 废话不
  • Epoxy - 在RecyclerView中构建复杂界面
    Diffing 对于复杂数据结构支持的多个视图类型展示在屏幕上, Epoxy此时是尤其有用的. 在这些场景中, 数据可能会被网络请求, 异步 Observable, 用
  • Android性能优化的详细介绍

    Android性能优化的详细介绍
    性能优化是一个app很重要的一部分,一个性能优良的app从被下载到启动到使用都能给用户到来很好的体验。自然我们做性能优化也是从被下
  • Android进阶宝典-插件化2(Hook启动插件中四大组件

    Android进阶宝典-插件化2(Hook启动插件中四大组件
    在上一节,我们主要介绍了如果通过反射来加载插件中的类,调用类中的方法;既然插件是一个apk,其实最重要的是启动插件中的Activity、
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计