编程Android 开发者

late-initialized 属性('lateinit var')在 Kotlin 中是如何工作的,它与可为空属性有什么区别,以及在什么情况下应该使用它?

用 Hintsage AI 助手通过面试

答案

问题的历史

Kotlin 注重类型安全并避免空指针,但经常会遇到延迟(late)初始化变量的需求,例如,在使用依赖注入或 Android Activity 时。为此,添加了 lateinit 修饰符。

问题

普通属性要求强制初始化或者必须是可为空的,这在保证延迟但强制初始化时并不方便。使用可为空类型会使代码变得复杂并需要额外的空检查。

解决方案

lateinit 允许创建在一段时间内未初始化的属性,但承诺编译器将在第一次使用之前初始化它。可以在构造函数中以外的地方进行初始化。

class UserViewModel { lateinit var repository: UserRepository fun onCreate() { repository = UserRepository() } fun getData() = repository.load() }

主要特点:

  • 允许不使用 null,同時延迟初始化
  • 在访问未初始化的 lateinit 属性时抛出 UninitializedPropertyAccessException
  • 仅适用于 var,不适用于原始类型(Int、Double 等)

影响性问题。

可以将 lateinit 应用于 val 属性吗?

不可以。lateinit 仅适用于 var,因为 val 必须立即初始化一次或通过 getter。

lateinit 是否适用于 Int、Boolean、Double 和其他原始类型?

不适用。仅适用于对象引用类型。对于原始类型,请使用可为空类型。

如果在初始化之前访问 lateinit 属性,会发生什么?

Kotlin 会抛出 UninitializedPropertyAccessException:

lateinit var foo: String println(foo) // 异常

常见错误和反模式

  • 忘记初始化 lateinit 属性 — 应用程序将崩溃
  • 在需要可为空语义和 isInitialized 检查的地方使用
  • 用于原始类型和 val 的应用

生活中的示例

负面案例

开发人员声明了 lateinit var item: String,但在第一次调用方法 getItem 之前未初始化。结果:应用程序崩溃。

优点:

  • 如果一切正确初始化,避免可为空类型

缺点:

  • 混淆生命周期造成运行时异常的风险

正面案例

Android Activity:lateinit var presenter,在 onCreate 中初始化。在所有生命周期方法中使用 presenter 是安全的:不需要可为空性。

优点:

  • 提高可读性,避免不必要的检查
  • 保证强制初始化

缺点:

  • 必须明确关注初始化的调用顺序