Kotlin委托属性
外观
Kotlin委托属性[编辑 | 编辑源代码]
Kotlin委托属性(Delegated Properties)是一种强大的语言特性,允许开发者将属性的访问逻辑(getter/setter)委托给另一个对象处理。通过这种方式,可以复用属性逻辑,减少样板代码,并实现更灵活的属性行为。
介绍[编辑 | 编辑源代码]
Kotlin中的属性通常由字段和访问器(getter/setter)组成。委托属性通过`by`关键字将属性的读写操作委托给一个实现了特定接口的对象(称为委托对象)。Kotlin标准库提供了几种内置委托(如`lazy`、`observable`),同时也支持自定义委托。
委托属性的核心接口是:
- `ReadOnlyProperty<in R, out T>`:用于只读属性(`val`)
- `ReadWriteProperty<in R, T>`:用于可变属性(`var`)
基本语法[编辑 | 编辑源代码]
定义一个委托属性的基本语法如下:
class Example {
var delegatedProperty: String by Delegate()
}
其中`Delegate`是一个实现了`ReadWriteProperty<Any?, String>`接口的类。
内置委托示例[编辑 | 编辑源代码]
Kotlin标准库提供了几种常用的委托:
lazy 委托[编辑 | 编辑源代码]
`lazy`用于延迟初始化属性,仅在第一次访问时计算值:
val lazyValue: String by lazy {
println("Computed!")
"Hello"
}
fun main() {
println(lazyValue) // 输出: Computed! \n Hello
println(lazyValue) // 仅输出: Hello(值已缓存)
}
observable 委托[编辑 | 编辑源代码]
`observable`允许在属性值改变时收到通知:
var observedValue: String by Delegates.observable("<初始值>") {
property, oldValue, newValue ->
println("${property.name} 从 $oldValue 变为 $newValue")
}
fun main() {
observedValue = "第一次修改" // 输出: observedValue 从 <初始值> 变为 第一次修改
observedValue = "第二次修改" // 输出: observedValue 从 第一次修改 变为 第二次修改
}
自定义委托[编辑 | 编辑源代码]
要创建自定义委托,需要实现`ReadWriteProperty`接口:
class RangeRegulator(
initialValue: Int,
private val minValue: Int,
private val maxValue: Int
) : ReadWriteProperty<Any?, Int> {
private var fieldData = initialValue
override fun getValue(thisRef: Any?, property: KProperty<*>): Int {
return fieldData
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
if (value in minValue..maxValue) {
fieldData = value
} else {
throw IllegalArgumentException("值必须在 $minValue 和 $maxValue 之间")
}
}
}
class Player {
var level: Int by RangeRegulator(1, 1, 100)
}
fun main() {
val player = Player()
player.level = 50 // 正常赋值
println(player.level) // 输出: 50
player.level = 150 // 抛出异常: IllegalArgumentException
}
属性委托原理[编辑 | 编辑源代码]
Kotlin编译器会为委托属性生成以下代码(以`var prop: Type by Delegate()`为例):
对应的伪Java代码:
public final class Example {
private final Delegate delegate$delegate = new Delegate();
public Type getProp() {
return delegate$delegate.getValue(this, $$delegatedProperties[0]);
}
public void setProp(Type value) {
delegate$delegate.setValue(this, $$delegatedProperties[0], value);
}
}
实际应用案例[编辑 | 编辑源代码]
[编辑 | 编辑源代码]
Android开发中常用委托属性简化SharedPreferences访问:
class Preference<T>(
private val context: Context,
private val name: String,
private val defaultValue: T,
private val prefName: String = "default_prefs"
) : ReadWriteProperty<Any?, T> {
private val prefs by lazy {
context.getSharedPreferences(prefName, Context.MODE_PRIVATE)
}
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return when (defaultValue) {
is String -> prefs.getString(name, defaultValue) as T
is Int -> prefs.getInt(name, defaultValue) as T
is Boolean -> prefs.getBoolean(name, defaultValue) as T
else -> throw IllegalArgumentException("不支持的类型")
}
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
with(prefs.edit()) {
when (value) {
is String -> putString(name, value)
is Int -> putInt(name, value)
is Boolean -> putBoolean(name, value)
else -> throw IllegalArgumentException("不支持的类型")
}.apply()
}
}
}
// 使用示例
class Settings(context: Context) {
var darkModeEnabled by Preference(context, "dark_mode", false)
var userName by Preference(context, "user_name", "Guest")
}
案例2:视图绑定[编辑 | 编辑源代码]
在Android视图绑定中避免重复的findViewById调用:
fun <T : View> Activity.viewBinding(@IdRes id: Int): Lazy<T> {
return lazy { findViewById<T>(id) }
}
class MainActivity : AppCompatActivity() {
private val buttonSubmit by viewBinding<Button>(R.id.btn_submit)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
buttonSubmit.setOnClickListener { /*...*/ } // 首次访问时执行findViewById
}
}
数学表示[编辑 | 编辑源代码]
委托属性的行为可以用以下公式表示: 对于属性`p`委托给对象`d`:
- 读取操作:`p` ⇨ `d.getValue(thisRef, property)`
- 写入操作:`p = v` ⇨ `d.setValue(thisRef, property, v)`
其中:
- (反射属性信息)
总结[编辑 | 编辑源代码]
Kotlin委托属性的主要优势包括:
- 代码复用:共享属性访问逻辑
- 封装:隐藏复杂实现细节
- 声明式语法:通过`by`关键字简洁表达意图
- 灵活性:支持动态属性行为
通过合理使用标准库委托和自定义委托,可以显著提升代码的可维护性和表达力。