标准库属性委托
源:Kotlin standard library - Delegates
Kotlin 的委托属性(Delegated Properties)机制允许我们将属性的 getter/setter 逻辑委托给另一个对象处理。标准库提供了一组开箱即用的委托实现,覆盖了惰性加载、观察者模式、Map 映射等常见模式。
惰性初始化 (Lazy)
by lazy 是最常用的委托,用于推迟对象的初始化,直到属性第一次被访问。
基础用法
kotlin
val heavyObject: HeavyClass by lazy {
println("初始化中...")
HeavyClass()
}
fun main() {
println("开始")
// 第一次访问:执行 lambda,打印 "初始化中..."
println(heavyObject)
// 第二次访问:直接返回缓存的实例
println(heavyObject)
}线程安全模式 (Thread Safety Modes)
lazy 函数接受一个可选的 LazyThreadSafetyMode 参数,这对性能至关重要。
kotlin
// 线程安全。
// 使用 synchronized 锁,保证初始化块只在单一线程执行一次。
val safe by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
compute()
}kotlin
// 线程安全。
// 允许多个线程同时执行初始化块,但只有第一个完成的结果会被赋值给属性。
// 适用于计算开销不大,但不仅限于单线程环境的场景。
val pub by lazy(LazyThreadSafetyMode.PUBLICATION) {
compute()
}kotlin
// ❌ 线程不安全!
// 没有任何锁开销。
// 如果你能确保该属性只会在单线程(如 Android 的主线程)中访问,
// 使用此模式可以获得最佳性能。
val fast by lazy(LazyThreadSafetyMode.NONE) {
bindViews()
}可观察属性 (Observable)
Delegates.observable 允许你在属性值发生变化时执行回调(例如打印日志、触发 UI 更新)。
kotlin
import kotlin.properties.Delegates
var userState: String by Delegates.observable("<initial>") { property, oldValue, newValue ->
println("属性 ${property.name} 从 $oldValue 变更为 $newValue")
}
userState = "Loading" // 输出变更日志
userState = "Success"否决属性 (Vetoable)
Delegates.vetoable 是 observable 的变体,它允许你在赋值发生前进行拦截。如果 Lambda 返回 false,则赋值被拒绝,属性保持原值。
kotlin
var age: Int by Delegates.vetoable(0) { property, oldValue, newValue ->
// 只有新值大于旧值时才允许修改(模拟年龄只能增长)
newValue > oldValue
}
age = 10 // 成功
println(age) // 10
age = 5 // 失败(5 < 10)
println(age) // 10Map 映射 (Map Delegation)
你可以直接将属性的值存储在一个 Map 中。这在解析动态数据(如 JSON 解析、Gradle 配置对象)时非常有用。
kotlin
class User(val map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}
val data = mapOf("name" to "John", "age" to 25)
val user = User(data)
println(user.name) // Johnkotlin
class MutableUser(val map: MutableMap<String, Any?>) {
var name: String by map
var age: Int by map
}
val data = mutableMapOf<String, Any?>()
val user = MutableUser(data)
user.name = "Doe"
println(data["name"]) // "Doe" - 自动更新到底层 Map延迟非空 (NotNull)
Delegates.notNull 允许定义一个非空类型的属性,但不需要在构造函数中初始化。这看起来和 lateinit var 很像。
kotlin
var config: Config by Delegates.notNull()
fun init() {
config = loadConfig()
}
fun run() {
// 如果在 init() 之前调用,抛出 IllegalStateException
println(config.url)
}VS lateinit
| 特性 | lateinit var | Delegates.notNull() |
|---|---|---|
| 适用类型 | 仅引用类型 (Class) | 任何类型 (包括 Int, Long 等原生类型) |
| 底层实现 | 字段直接存储 (Field) | 委托对象封装 |
| 依赖注入 | 框架友好 (Dagger/Hilt) | 通常不用于 DI |
| 性能 | 极快 (无额外开销) | 较慢 (每次访问都有函数调用开销) |
建议
对于对象类型,优先使用 lateinit。仅在必须对原生类型(如 Int)进行延迟初始化时,才使用 Delegates.notNull()。