在 Kotlin 中,自定义属性委托允许开发者将属性的读写逻辑封装到独立对象中,实现代码复用和解耦。以下是实现自定义委托的完整指南:
一、自定义委托的实现方式
1. 通过 operator 方法实现
-
只读属性(
val) :实现getValue方法。class StringDelegate { operator fun getValue(thisRef: Any?, property: KProperty<*>): String { return "固定值或动态逻辑" } } -
可变属性(
var) :需同时实现getValue和setValue。class LoggingDelegate<T>(private var value: T) { operator fun getValue(thisRef: Any?, property: KProperty<*>): T { println("读取属性 ${property.name} → $value") return value } operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: T) { println("设置属性 ${property.name}: $value → $newValue") value = newValue } }
2. 通过接口实现
-
只读属性:实现
ReadOnlyProperty接口。class ReadOnlyDelegate : ReadOnlyProperty<Any, String> { override fun getValue(thisRef: Any, property: KProperty<*>): String { return "接口实现返回值" } } -
可变属性:实现
ReadWriteProperty接口。class ReadWriteDelegate : ReadWriteProperty<Any, Int> { private var _value = 0 override fun getValue(thisRef: Any, property: KProperty<*>): Int = _value override fun setValue(thisRef: Any, property: KProperty<*>, value: Int) { _value = value } }
二、使用自定义委托
1. 基本语法
class Example {
val readOnlyProp: String by StringDelegate() // 只读委托
var mutableProp: Int by ReadWriteDelegate() // 可变委托
var loggedProp: String by LoggingDelegate("初始值") // 带日志的委托
}
2. 实际应用示例
场景:属性校验委托
class RangeValidator(private var min: Int, private var max: Int) {
private var _value = min
operator fun getValue(thisRef: Any?, property: KProperty<*>): Int = _value
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
if (value in min..max) {
_value = value
} else {
throw IllegalArgumentException("${property.name} 超出范围 [$min, $max]")
}
}
}
class Config {
var percentage: Int by RangeValidator(0, 100) // 限定百分比范围
}
// 使用
val config = Config()
config.percentage = 50 // 正常
config.percentage = 120 // 抛出异常
三、核心注意事项
| 要点 | 说明 |
|---|---|
| 方法签名匹配 | getValue/setValue 参数类型必须严格遵循标准(thisRef: Any?, property: KProperty<*>)58。 |
| 可见性要求 | 若委托类与宿主类不在同一模块,需确保方法可见性为 public38。 |
| 性能优化 | 高频访问属性时,避免在委托中执行复杂逻辑,以减少额外开销58。 |
| 接口 vs 方法 | 接口实现更规范,operator 方法更灵活(如支持泛型参数)35。 |
四、典型应用场景
- 数据校验
如范围检查、格式验证(邮箱、URL 等)5。 - 日志记录
拦截属性读写操作,生成审计日志8。 - 数据绑定
实现属性与 UI 控件、数据库字段的动态同步35。 - 缓存管理
延迟加载资源或缓存计算结果(类似lazy的高级扩展)8。