Skip to content

委托机制 (Delegation)

源:Kotlin 委托属性官方参考

委托模式是实现继承更轻量的替代方案。Kotlin 在语言层面原生支持 类委托属性委托

类委托 (Class Delegation)

通过 by 关键字,将接口的实现委托给另一个对象。这是一种优雅的 装饰者模式 (Decorator Pattern) 实现。

kotlin
interface Base {
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

// Derived 类实现了 Base 接口,但将所有调用转发给 b
class Derived(b: Base) : Base by b

fun main() {
    val b = BaseImpl(10)
    Derived(b).print() // 输出 10
}

覆盖机制

如果 Derived 自己重写了 print 方法,编译器会优先使用重写的方法,而不是转发给委托对象。

属性委托 (Delegated Properties)

将属性的 Getter/Setter 逻辑封装在独立的类中。

标准委托库

kotlin
// 惰性初始化
// 默认线程安全 (SYNCHRONIZED)
val heavyData: String by lazy {
    println("Computing...")
    "Result"
}
kotlin
// 观察者模式:自动回调
var user: User by Delegates.observable(User()) { prop, old, new ->
    Log.d("User", "Updated: $old -> $new")
}
kotlin
// 拦截赋值:返回 false 则赋值失败
var age: Int by Delegates.vetoable(0) { _, _, new ->
    new >= 0 // 仅允许非负数
}

实战:Android 扩展应用

Android Jetpack 大量使用了属性委托。

kotlin
class MyFragment : Fragment() {
    // 1. ViewModel 委托
    val viewModel: MyViewModel by viewModels()

    // 2. Navigation 参数委托 (Safe Args)
    val args: MyFragmentArgs by navArgs()
    
    // 3. 自定义 SharedPreferences 委托
    var isFirstRun by PreferenceDelegate(context, "first_run", true)
}

映射委托 (Map)

将属性存储在 Map 中。这在解析动态 JSON 或作为配置项容器时非常有用。

kotlin
class User(val map: Map<String, Any?>) {
    val name: String by map
    val age: Int     by map
}

val user = User(mapOf(
    "name" to "John",
    "age"  to 25
))

自定义委托原理

一个类只要定义了符合约定的 getValue (和 setValue) 操作符函数,就可以作为委托。

kotlin

// 示例:定义一个只能在 Context 下使用的资源委托

class ResourceDelegate(val resId: Int) {

    operator fun getValue(thisRef: Context, property: KProperty<*>): String {

        return thisRef.getString(resId)

    }

}



// 扩展函数形式的委托提供者

fun Context.stringResource(resId: Int) = ResourceDelegate(resId)



// 在 Activity/Context 中使用

val title by stringResource(R.string.title)