Kotlin 的延迟初始化委托属性 by lazy

Kotlin 的延迟初始化委托属性 by lazy 详细解释:


📖 代码拆解

kotlin 复制代码
private val viewModel: NewsViewModel by lazy {

可以拆分为几个部分:

部分 含义
private 私有访问修饰符,外部无法访问
val 只读属性(类似 Java 的 final)
viewModel 属性名称
: NewsViewModel 属性类型
by lazy 使用延迟初始化委托

🔄 lazy 的工作原理

1. 延迟初始化

kotlin 复制代码
// 使用 lazy 的写法
private val viewModel: NewsViewModel by lazy {
    AppModule.provideNewsViewModel()
}

等价的 Java 写法:

java 复制代码
private NewsViewModel viewModel;

private NewsViewModel getViewModel() {
    if (viewModel == null) {
        viewModel = AppModule.provideNewsViewModel();
    }
    return viewModel;
}

2. 执行时机

时机 说明
声明时 ❌ 不执行
第一次访问时 ✅ 执行初始化
后续访问 ✅ 直接返回缓存值

例子:

kotlin 复制代码
private val viewModel: NewsViewModel by lazy {
    println("初始化 ViewModel")  // 这行只会在第一次访问时执行
    AppModule.provideNewsViewModel()
}

fun onCreate() {
    println("onCreate 开始")
    // viewModel 还没初始化
    println(viewModel.newsList)  // 此时才初始化,打印 "初始化 ViewModel"
    println(viewModel.newsList)  // 直接返回缓存,不打印
}

💡 为什么使用 lazy

1. 性能优化

kotlin 复制代码
// 不使用 lazy(每次创建 MainActivity 都会初始化)
private val viewModel = AppModule.provideNewsViewModel()  // 立即执行

// 使用 lazy(只在第一次访问时初始化)
private val viewModel: NewsViewModel by lazy {
    AppModule.provideNewsViewModel()  // 延迟执行
}

2. 避免循环依赖

kotlin 复制代码
class MainActivity {
    private val viewModel by lazy { NewsViewModel(repository) }
    private val repository by lazy { NewsRepository() }
    
    // 如果不用 lazy,repository 还没初始化就传给 viewModel 会报错
}

3. 简化代码

kotlin 复制代码
// 不使用 lazy
private lateinit var viewModel: NewsViewModel

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    viewModel = AppModule.provideNewsViewModel()
}

// 使用 lazy
private val viewModel: NewsViewModel by lazy {
    AppModule.provideNewsViewModel()
}

🎯 在项目中的实际应用

完整代码:

kotlin 复制代码
class MainActivity : ComponentActivity() {

    // 延迟初始化 ViewModel
    private val viewModel: NewsViewModel by lazy {
        AppModule.provideNewsViewModel()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            AndroidFireTheme {
                // 第一次访问 viewModel 时才初始化
                val selectedNews by viewModel.selectedNews.collectAsState()
                
                if (selectedNews != null) {
                    DetailScreen(news = selectedNews!!, onBack = { ... })
                } else {
                    HomeScreen(viewModel = viewModel, onNewsClick = { ... })
                }
            }
        }
    }
}

🔄 lazy vs lateinit 对比

特性 lazy lateinit
类型 只读(val 可变(var
初始化时机 第一次访问时 手动赋值
线程安全 默认线程安全 不安全
可空性 不可空 可空
适用场景 只读属性、延迟初始化 可变属性、依赖注入

💻 实际例子

例子1:普通延迟初始化

kotlin 复制代码
private val expensiveObject: ExpensiveObject by lazy {
    println("开始初始化...")
    Thread.sleep(2000)  // 模拟耗时操作
    ExpensiveObject()
}

fun test() {
    println("1")
    println(expensiveObject)  // 第一次访问,开始初始化
    println("2")
    println(expensiveObject)  // 第二次访问,直接返回缓存
    println("3")
}

// 输出:
// 1
// 开始初始化...
// ExpensiveObject@123
// 2
// ExpensiveObject@123
// 3

例子2:线程安全的 lazy

kotlin 复制代码
private val threadSafeValue by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
    // 确保只有一个线程执行初始化
    "thread-safe"
}

🎓 总结

要点 说明
作用 延迟初始化,只在第一次访问时执行
优势 性能优化、避免循环依赖、代码简洁
线程安全 默认线程安全(SYNCHRONIZED 模式)
适用场景 只读属性、初始化耗时、依赖其他属性

简单理解by lazy 就是"懒加载",用的时候才初始化,不用就不初始化,节省资源!

相关推荐
Kapaseker3 小时前
我为什么让 Toast 多弹了一次
android·kotlin
赏金术士3 小时前
Kotlin Flow 完全指南
android·开发语言·kotlin
帅次5 小时前
测试分层:JVM 单测、ViewModel 测试与 Compose UI Test
android·jvm·ui·kotlin·compose·modifier
赏金术士14 小时前
Kotlin 习题集 · 高级篇
android·开发语言·kotlin
pengyu1 天前
【Kotlin 协程修仙录 · 金丹境 · 中阶】 | 启动密法:CoroutineStart 四种模式与底层调度玄机
android·kotlin
UXbot1 天前
AI一次生成iOS和Android双端原型功能详解
android·前端·ios·kotlin·交互·swift
赏金术士1 天前
Kotlin 习题集 · 进阶篇
java·数据库·kotlin
赏金术士1 天前
Kotlin 习题集 · 基础篇
android·开发语言·kotlin
Kapaseker1 天前
最简单的 Compose 动画 — animateDpAsState
android·kotlin