委托机制 (Delegation)
委托模式是实现继承更轻量的替代方案。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)