请解释 Kotlin 中 by lazy 委托的实现原理,并说明它与 lateinit 在初始化时机、线程安全和适用场景上的主要区别。
考察说明
考查对 Kotlin 延迟初始化机制 by lazy 的内部实现原理及其与 lateinit 差异的理解。
回答思路
- 【回答框架 1】by lazy 是 Kotlin 标准库中的属性委托,它将属性初始化推迟到首次访问时。lazy 函数接收一个初始化 lambda,并返回一个 Lazy<T> 实例,该实例内部保存线程安全模式和初始化回调。
- 【回答框架 2】Lazy 接口根据指定的线程安全模式采用不同实现:默认 SYNCHRONIZED 使用同步锁保证线程安全,PUBLICATION 通过 CAS 允许多线程竞争但只执行一次,NONE 不设同步,仅适用于单线程环境。首次访问时,getValue 方法会被调用,执行初始化并将结果缓存。
- 【回答框架 3】与 lateinit 相比,by lazy 适用于 val 属性,初始化代码在首次访问时执行,且能处理非空类型但可自定义初始化逻辑;而 lateinit 适用于 var 属性,通常用于依赖注入或延迟赋值,使用前必须手动确保已赋值,否则抛出 UninitializedPropertyAccessException,且无法用于原生类型和可空属性。
- 【回答框架 4】选择依据:若属性只读且需要延迟计算,使用 by lazy;若属性需要在生命周期中的某个阶段被外部赋值,且类型不可空,则选择 lateinit。
- 【关键点 1】by lazy 使用委托机制,首次访问时执行初始化并使用缓存。
- 【关键点 2】默认 SYNCHRONIZED 模式线程安全,PUBLICATION 和 NONE 各有适用场景。
- 【关键点 3】lateinit 用于 var 且非空类型,提前使用会抛异常,无线程安全保障。
- 【关键点 4】by lazy 支持实现复杂的初始化逻辑并保证只执行一次。
- 【易错点 1】将未初始化就访问 lateinit 属性误认为返回 null,实际会抛出 UninitializedPropertyAccessException。
- 【易错点 2】对多线程环境使用 NONE 模式的 by lazy 可能导致重复初始化,应优先使用默认模式。