Skip to content

标准库属性委托

源: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.vetoableobservable 的变体,它允许你在赋值发生进行拦截。如果 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) // 10

Map 映射 (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) // John
kotlin
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 varDelegates.notNull()
适用类型仅引用类型 (Class)任何类型 (包括 Int, Long 等原生类型)
底层实现字段直接存储 (Field)委托对象封装
依赖注入框架友好 (Dagger/Hilt)通常不用于 DI
性能极快 (无额外开销)较慢 (每次访问都有函数调用开销)

建议

对于对象类型,优先使用 lateinit。仅在必须对原生类型(如 Int)进行延迟初始化时,才使用 Delegates.notNull()