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