编程Android开发者

在Kotlin中,'lateinit'和'lazy'是什么意思?请解释它们之间的区别、使用细节、限制,并提供相应的代码示例。

用 Hintsage AI 助手通过面试

回答

在Kotlin中有两种不同的方式来延迟初始化属性:lateinitlazy

lateinit — 应用于稍后初始化的变量(var)的修饰符,通常通过依赖注入(DI)或在初始化块中进行初始化。仅适用于非静态的非空类型变量。在初始化之前访问会抛出UninitializedPropertyAccessException异常。

class MyClass { lateinit var data: String fun init() { data = "Hello!" } }

lazy — 应用于val 属性的特殊委托(仅限只读)。初始化表达式在第一次访问变量时计算。默认情况下对多线程访问是安全的。

val config: Config by lazy { loadConfigFromFile() }
  • lateinit不适用于原始类型和val
  • lazy不允许重新初始化。
  • lateinit用于那些保证稍后会被赋值的变量(例如,通过DI框架或在Android生命周期中的片段)。
  • lazy用于延迟计算,当值的计算成本高且可能不会被请求时。

设陷阱的问题

可以将lateinit用于Int类型的属性吗?

答案: 不可以。lateinit仅适用于对象的非空类型。对于原始类型和可空类型 — 不可以。尝试声明lateinit var a: Int会导致编译错误。

由于不了解主题细节而导致的实际错误示例


故事

在一个教育项目中,声明了一个配置变量为lateinit var config: Config?,但是编译器不允许这样做 — 节省了时间,但必须弄清楚为什么不如预期工作。


故事

在Android应用程序中,忘记在调用之前检查lateinit属性是否初始化。这导致应用程序在生产环境中崩溃 — 设计师在onCreateView之前尝试访问属性。额外的检查提供了帮助。


故事

在使用多线程时,开发人员对耗资源的对象初始化应用了by lazy,而没有传递参数,误以为默认委托不是线程安全的。最终在发布版本中出现了竞争条件的问题。通过传递相应的参数解决了这个问题:by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { ... }.