属性委托(by 关键字)
1. 什么是属性委托?
属性委托(Property Delegation)是 Kotlin 中一种强大的特性,允许将属性的 getter 和 setter 逻辑委托给另一个对象(称为委托对象)。通过 by 关键字实现,可以简化代码并实现逻辑复用。
核心思想
- 解耦:将属性的存储或计算逻辑与类分离。
- 复用:通过委托对象统一管理多个属性的行为(如延迟初始化、监听变化等)。
2. 基本语法
class Example {
var property: Type by Delegate() // 委托给 Delegate 类的实例
}
- 委托对象必须实现
ReadWriteProperty(可变属性)或ReadOnlyProperty(只读属性)接口。
3. 自定义属性委托
示例:实现一个简单的委托
class SimpleDelegate(private var value: String) : ReadWriteProperty<Any?, String> {
override fun getValue(thisRef: Any?, property: KProperty<*>): String {
println("读取属性 ${property.name}")
return value
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("设置属性 ${property.name} 为 $value")
this.value = value
}
}
class User {
var name: String by SimpleDelegate("默认值")
}
// 使用
fun main() {
val user = User()
println(user.name) // 输出:读取属性 name → "默认值"
user.name = "Kotlin" // 输出:设置属性 name 为 Kotlin
}
4. 标准库中的委托属性
Kotlin 标准库提供了几种常用的委托实现:
4.1 lazy:延迟初始化
val lazyValue: String by lazy {
println("首次访问时计算")
"结果"
}
fun main() {
println(lazyValue) // 输出:首次访问时计算 → "结果"
println(lazyValue) // 直接返回缓存值 "结果"
}
- 线程安全(默认使用
LazyThreadSafetyMode.SYNCHRONIZED)。
4.2 Delegates.observable:监听属性变化
import kotlin.properties.Delegates
var observedValue: Int by Delegates.observable(0) { _, old, new ->
println("值从 $old 变为 $new")
}
fun main() {
observedValue = 10 // 输出:值从 0 变为 10
}
4.3 Delegates.vetoable:拦截属性赋值
var maxValue: Int by Delegates.vetoable(0) { _, old, new ->
new > old // 只有新值大于旧值时才允许赋值
}
fun main() {
maxValue = 10
println(maxValue) // 10
maxValue = 5 // 赋值失败(无输出)
println(maxValue) // 仍为 10
}
5. 使用场景与优势
- 延迟加载:减少资源消耗(如数据库连接、大对象初始化)。
- 观察者模式:自动响应属性变化(如 UI 更新)。
- 统一验证逻辑:集中管理属性的合法性检查。
- 简化代码:避免重复的
getter/setter模板代码。
6. 注意事项
- 委托对象的生命周期:需确保委托对象在属性访问期间有效。
- 性能开销:委托可能引入额外的函数调用,但在大多数场景下可忽略。
- 线程安全:自定义委托时需考虑并发访问问题。
7. 实战练习
尝试实现一个委托,要求:
- 属性值必须为非空字符串。
- 每次赋值时记录日志(包括时间戳)。
提示:结合 ReadWriteProperty 和 LocalDateTime 实现。
// 参考答案
class NonNullStringDelegate : ReadWriteProperty<Any?, String> {
private var value: String = ""
override fun getValue(thisRef: Any?, property: KProperty<*>): String {
return value
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
require(value.isNotEmpty()) { "属性值不能为空" }
println("${LocalDateTime.now()} - 设置属性 ${property.name} 为 $value")
this.value = value
}
}
