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 就是"懒加载",用的时候才初始化,不用就不初始化,节省资源!

相关推荐
JMchen9 小时前
第 9 篇|网络请求基础 —— 从 HttpURLConnection 到 OkHttp
kotlin·android studio
hai_android1 天前
Kotlin Flow combine 源码剖析:一个 Channel 如何优雅合并多条流
android·java·kotlin
敲代码的瓦龙1 天前
Jetpack?ViewModel!!!
android·开发语言·kotlin·android-studio
swithun1 天前
不用 WebView,我用 Kotlin + Compose Multiplatform 重写 Mermaid,并做了 2048 组对拍
android·开源·kotlin
JMchen1 天前
第 8 篇|本地存储技术选型 —— SP 与文件读写
kotlin·android studio
码农哈丁2 天前
从 JVM 到 Cargo:把整个 Kotlin 项目移植到 Rust 的踩坑实录
jvm·rust·kotlin
Kapaseker2 天前
写过 4000 行 ViewModel 后,我开始这样拆 Compose 页面
android·kotlin
新时代牛马3 天前
cyclictest 毛刺从哪来?从ftrace、latencytop、perf到IRQ/调度延迟定位
android·开发语言·python·kotlin
光电的一只菜鸡3 天前
为什么调整LSC会对AF产生影响
android·开发语言·kotlin
hai_android3 天前
FusibleFlow:Kotlin Flow 的融合机制原理
android·kotlin