Kotlin by lazy和lateinit的使用及区别

在 Kotlin 中,lateinit 和 by lazy 都可以用来延迟初始化变量,但它们有不同的适用场景和使用方式。下面详细介绍它们的用法和区别。

1.lateinit

lateinit 关键字用于延迟初始化 可变 属性 (var),主要用于那些在声明时不能立即初始化,但保证在使用前会完成初始化的属性。lateinit 只能用于非空的 可变变量 ,且只能用于 类的属性(不能用于局部变量或常量)。

使用场景

lateinit 通常用于 Android 的 Context、View、Presenter 等需要在声明时无法立即赋值,但稍后会赋值的情况。

示例代码
Kotlin 复制代码
class Example {
    lateinit var text: String // 延迟初始化的变量

    fun initializeText() {
        text = "Hello, World!"
    }

    fun printText() {
        if (this::text.isInitialized) { // 检查是否已经初始化
            println(text)
        } else {
            println("text 未初始化")
        }
    }
}

fun main() {
    val example = Example()
    example.initializeText()
    example.printText() // 输出:Hello, World!
}
注意事项
  • 只适用于可变变量 :lateinit 只能用于 var 变量,不能用于 val。
  • 非空类型 :lateinit 只能用于非空类型,不能用于原始数据类型(如 Int、Double 等)。
  • 是否初始化检查 :可以使用 this::property.isInitialized 检查属性是否已初始化,避免直接访问未初始化的变量引发异常。

2. by lazy

by lazy 适用于 不可变属性 (val),它的初始化只会在第一次被访问时触发,且结果会被缓存下来,以后每次访问时都返回相同的值。by lazy 可以用来延迟初始化那些计算量大或依赖其他数据的属性,通常适用于线程安全的场景。by lazy本身是一种属性委托。属性委托的关键字是by。

使用场景

适合用来初始化 只读变量,比如配置文件加载、耗时计算结果等。

示例代码
Kotlin 复制代码
class Example {
    val text: String by lazy {
        println("初始化 text")
        "Hello, Lazy!"
    }

    fun printText() {
        println(text) // 第一次访问时初始化
    }
}

fun main() {
    val example = Example()
    example.printText() // 输出:初始化 text\nHello, Lazy!
    example.printText() // 输出:Hello, Lazy!(不会再初始化)
}
注意事项
  • 只适用于只读变量 :by lazy 只能用于 val 变量,表示它是不可变的。
  • 线程安全 :by lazy 的默认模式是线程安全的(相当于 LazyThreadSafetyMode.SYNCHRONIZED),也可以选择其他模式,如 LazyThreadSafetyMode.PUBLICATION 或 LazyThreadSafetyMode.NONE。
  • 延迟计算:只有在属性第一次被访问时才会初始化。

3. lateinit 和 by lazy 的区别

特性 lateinit by lazy
适用类型 var 可变变量 val 不可变变量
适用数据类型 只能用于非空对象类型 适用于任何类型,包括基础数据类型
初始化时机 在使用前需要手动赋值 第一次访问时自动初始化
初始化次数 可以被多次赋值 只会被初始化一次
线程安全性 无内置的线程安全 默认是线程安全的(可更改模式)
使用场景 Android 中的 Context、View 等 计算量大的只读变量,懒加载
是否可检查初始化状态 可以使用 isInitialized 检查 无法检查是否已初始化

总结

  • 使用 lateinit :适用于那些需要稍后赋值且会被多次修改的对象属性,比如 Android 中的 Context 或 View。
  • 使用 by lazy:适用于那些不需要立即初始化,且一旦初始化后不再改变的只读属性,比如计算密集型或一次性加载的属性。
  • 一般来说的话,lateinit一定要记得初始化,不然后面会爆异常UninitializedPropertyAccessException
相关推荐
程序 代码狂人1 小时前
切片表与分区表的区别(数据开发面试版)
java·大数据·开发语言·数据仓库·算法
警醒与鞭策2 小时前
Codex桌面版与Claude桌面版使用设置
android·unity·ai·性能优化
meiying88382 小时前
快递单号批量查询Python脚本:从手动到自动化的完整实现
开发语言·python·自动化
@大迁世界3 小时前
告别 Go,拥抱 Rust
开发语言·golang·rust
SEO_juper3 小时前
Node.js 重定向链检测工具:301 跳了几跳、有没有成环,一次看清
xml·开发语言·python·node.js·json·bash
学逆向的3 小时前
C 指针常量与常量指针
c语言·开发语言·网络安全
低调滴开发4 小时前
3 行代码写崩一个函数?切片才是 Rust 字符串处理的正确姿势
开发语言·后端·rust
CodeYp20194 小时前
第15讲:二叉搜索树
开发语言·c++·二叉搜索树
猫猫.1534 小时前
HashTable、HashMap、ConcurrentHashMap 核心区别总结
java·开发语言·java-ee
传奇开心果4 小时前
【Compose Multiplatform跨端声明式UI实用技术】第19课:多平台部署与发布
android·kotlin