Kotlin 委托功能详解
2026/3/15大约 13 分钟
目录
委托概述
Kotlin 中的委托是一种设计模式的语言级实现,核心思想是将一个类的部分功能委托给另一个类来完成。Kotlin 通过关键字 by 在语言层面优雅地支持了两种主要的委托模式:
- 类委托:将接口的实现委托给另一个对象
- 属性委托:将属性的访问逻辑委托给一个委托对象
委托的优势:
- 避免继承的耦合性
- 实现代码复用
- 提高代码可读性和可维护性
- 支持组合优于继承的设计原则
类委托
类委托允许一个类将接口的实现委托给另一个对象,而不是自己实现所有方法。
基本语法
interface Base {
fun doSomething()
fun doSomethingElse()
}
class BaseImpl(val x: Int) : Base {
override fun doSomething() { println("BaseImpl.doSomething(): x") }
override fun doSomethingElse() { println("BaseImpl.doSomethingElse(): x") }
}
class Derived(b: Base) : Base by b
fun main() {
val base = BaseImpl(10)
val derived = Derived(base)
derived.doSomething() // 输出: BaseImpl.doSomething(): 10
derived.doSomethingElse() // 输出: BaseImpl.doSomethingElse(): 10
}重写委托方法
class Derived(b: Base) : Base by b {
// 重写部分方法
override fun doSomething() {
println("Derived.doSomething()")
// 可以调用委托对象的方法
// b.doSomething()
}
}
fun main() {
val base = BaseImpl(10)
val derived = Derived(base)
derived.doSomething() // 输出: Derived.doSomething()
derived.doSomethingElse() // 输出: BaseImpl.doSomethingElse(): 10
}多接口委托
interface A {
fun foo()
}
interface B {
fun bar()
}
class AImpl : A {
override fun foo() { println("AImpl.foo()") }
}
class BImpl : B {
override fun bar() { println("BImpl.bar()") }
}
class MultiDelegate(a: A, b: B) : A by a, B by b
fun main() {
val multi = MultiDelegate(AImpl(), BImpl())
multi.foo() // 输出: AImpl.foo()
multi.bar() // 输出: BImpl.bar()
}类委托的应用场景
- 装饰器模式:在不修改原有类的情况下,为其添加额外功能
- 适配器模式:将一个接口转换为另一个接口
- 代理模式:控制对原始对象的访问
- 组合复用:通过组合实现功能复用,避免继承的局限性
属性委托
属性委托允许将属性的 getter 和 setter 逻辑委托给一个委托对象。
基本语法
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name}' in $thisRef")
}
}
class Example {
var p: String by Delegate()
}
fun main() {
val e = Example()
println(e.p) // 输出: Example@3b07d329, thank you for delegating 'p' to me!
e.p = "NEW"
// 输出: NEW has been assigned to 'p' in Example@3b07d329
println(e.p) // 输出: Example@3b07d329, thank you for delegating 'p' to me!
}委托对象的要求
对于只读属性(val),委托对象需要实现 getValue 方法:
operator fun getValue(thisRef: Any?, property: KProperty<*>): T对于可变属性(var),委托对象还需要实现 setValue 方法:
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T)其中:
thisRef:属性所属的对象property:属性的元数据value:要设置的新值
标准委托
Kotlin 标准库提供了几种常用的委托实现:
1. 惰性初始化(lazy)
lazy 委托用于延迟初始化属性,只有在首次访问时才会计算值。
// 基本用法
val lazyValue: String by lazy {
println("Computed!")
"Hello"
}
fun main() {
println(lazyValue) // 输出: Computed! Hello
println(lazyValue) // 输出: Hello (不再计算)
}
// 线程安全模式
val lazyValue1: String by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
// 线程安全,默认模式
"Value"
}
// 非线程安全模式
val lazyValue2: String by lazy(LazyThreadSafetyMode.NONE) {
// 非线程安全,适合单线程环境
"Value"
}
// publication 模式
val lazyValue3: String by lazy(LazyThreadSafetyMode.PUBLICATION) {
// 多线程环境下可能计算多次,但只使用第一个结果
"Value"
}
// 伴生对象中的单例模式
class SingletonDemo {
companion object {
val instance: SingletonDemo by lazy {
SingletonDemo()
}
}
}2. 可观察属性(observable)
observable 委托用于监听属性值的变化。
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("初始值") {
property, oldValue, newValue ->
println("${property.name} 从 $oldValue 变为 $newValue")
}
}
fun main() {
val user = User()
user.name = "Alice" // 输出: name 从 初始值 变为 Alice
user.name = "Bob" // 输出: name 从 Alice 变为 Bob
}3. 可否决属性(vetoable)
vetoable 委托允许在属性值变化时进行验证,如果验证失败则拒绝更改。
import kotlin.properties.Delegates
class Person {
var age: Int by Delegates.vetoable(18) {
property, oldValue, newValue ->
println("尝试将 ${property.name} 从 $oldValue 改为 $newValue")
// 年龄必须在 0-150 之间
newValue in 0..150
}
}
fun main() {
val person = Person()
println(person.age) // 输出: 18
person.age = 20 // 输出: 尝试将 age 从 18 改为 20
println(person.age) // 输出: 20
person.age = 200 // 输出: 尝试将 age 从 20 改为 200
println(person.age) // 输出: 20 (拒绝更改)
}4. Map 委托
Map 委托用于将属性存储在 Map 中,适用于动态属性。
// 只读 Map
class User(map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}
// 可变 Map
class MutableUser(map: MutableMap<String, Any?>) {
var name: String by map
var age: Int by map
}
fun main() {
// 只读 Map 示例
val user = User(mapOf(
"name" to "Alice",
"age" to 30
))
println(user.name) // 输出: Alice
println(user.age) // 输出: 30
// 可变 Map 示例
val mutableMap = mutableMapOf(
"name" to "Bob",
"age" to 25
)
val mutableUser = MutableUser(mutableMap)
println(mutableUser.name) // 输出: Bob
mutableUser.name = "Charlie"
println(mutableUser.name) // 输出: Charlie
println(mutableMap["name"]) // 输出: Charlie (Map 也被修改)
}自定义委托
1. 基本自定义委托
class CustomDelegate<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}: $newValue (旧值: $value)")
value = newValue
}
}
class Example {
var name: String by CustomDelegate("初始值")
var age: Int by CustomDelegate(18)
}
fun main() {
val ex = Example()
println(ex.name) // 输出: 获取 name: 初始值
ex.name = "Alice" // 输出: 设置 name: Alice (旧值: 初始值)
println(ex.name) // 输出: 获取 name: Alice
println(ex.age) // 输出: 获取 age: 18
}2. 带参数的自定义委托
class ValidatingDelegate<T>(
private var value: T,
private val validator: (T) -> Boolean
) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: T) {
if (validator(newValue)) {
value = newValue
} else {
throw IllegalArgumentException("无效值: $newValue")
}
}
}
fun <T> validated(initialValue: T, validator: (T) -> Boolean) = ValidatingDelegate(initialValue, validator)
class Person {
var email: String by validated("", {
it.contains("@")
})
var age: Int by validated(18, {
it in 0..150
})
}
fun main() {
val person = Person()
person.email = "alice@example.com" // 有效
person.age = 30 // 有效
try {
person.email = "invalid-email" // 无效
} catch (e: IllegalArgumentException) {
println(e.message) // 输出: 无效值: invalid-email
}
}3. 静态属性委托
class StaticDelegate {
companion object {
private var value: String = "静态值"
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return value
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: String) {
value = newValue
}
}
}
class Example {
companion object {
var staticProperty: String by StaticDelegate
}
}
fun main() {
println(Example.staticProperty) // 输出: 静态值
Example.staticProperty = "新静态值"
println(Example.staticProperty) // 输出: 新静态值
}4. 缓存委托
class CachingDelegate<T>(private val loader: () -> T) {
private var cachedValue: T? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return cachedValue ?: loader().also { cachedValue = it }
}
}
fun <T> cached(loader: () -> T) = CachingDelegate(loader)
class DataService {
val expensiveData: String by cached {
println("加载数据...")
// 模拟耗时操作
Thread.sleep(1000)
"加载的数据"
}
}
fun main() {
val service = DataService()
println(service.expensiveData) // 输出: 加载数据... 加载的数据
println(service.expensiveData) // 输出: 加载的数据 (使用缓存)
}进阶用法
1. 委托链
class FirstDelegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "FirstDelegate"
}
}
class SecondDelegate(private val firstDelegate: FirstDelegate) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "SecondDelegate wrapping ${firstDelegate.getValue(thisRef, property)}"
}
}
class Example {
val value: String by SecondDelegate(FirstDelegate())
}
fun main() {
val ex = Example()
println(ex.value) // 输出: SecondDelegate wrapping FirstDelegate
}2. 委托与反射
class ReflectiveDelegate(private val target: Any) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): Any? {
val field = target::class.java.getDeclaredField(property.name)
field.isAccessible = true
return field.get(target)
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Any?) {
val field = target::class.java.getDeclaredField(property.name)
field.isAccessible = true
field.set(target, value)
}
}
class Person(private var name: String, private var age: Int) {
override fun toString(): String {
return "Person(name='$name', age=$age)"
}
}
class PersonWrapper(person: Person) {
var name: String by ReflectiveDelegate(person)
var age: Int by ReflectiveDelegate(person)
}
fun main() {
val person = Person("Alice", 30)
val wrapper = PersonWrapper(person)
println(wrapper.name) // 输出: Alice
wrapper.age = 31
println(person) // 输出: Person(name='Alice', age=31)
}3. 委托与协程
import kotlinx.coroutines.*
class SuspendingDelegate<T>(private val loader: suspend () -> T) {
private var cachedValue: T? = null
private var job: Deferred<T>? = null
suspend operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return cachedValue ?: run {
job?.let { return it.await() }
val newJob = GlobalScope.async {
loader()
}
job = newJob
newJob.await().also {
cachedValue = it
job = null
}
}
}
}
fun <T> suspending(loader: suspend () -> T) = SuspendingDelegate(loader)
class DataRepository {
val data: String by suspending {
println("加载数据...")
delay(1000) // 模拟网络请求
"加载的数据"
}
}
fun main() = runBlocking {
val repo = DataRepository()
println(repo.data) // 输出: 加载数据... 加载的数据
println(repo.data) // 输出: 加载的数据 (使用缓存)
}4. 委托与泛型
class GenericDelegate<T>(private var value: T) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: T) {
value = newValue
}
}
class Example {
var stringValue: String by GenericDelegate("初始值")
var intValue: Int by GenericDelegate(42)
var listValue: List<String> by GenericDelegate(listOf("a", "b", "c"))
}
fun main() {
val ex = Example()
println(ex.stringValue) // 输出: 初始值
println(ex.intValue) // 输出: 42
println(ex.listValue) // 输出: [a, b, c]
ex.stringValue = "新值"
ex.intValue = 100
ex.listValue = listOf("x", "y", "z")
println(ex.stringValue) // 输出: 新值
println(ex.intValue) // 输出: 100
println(ex.listValue) // 输出: [x, y, z]
}Bad Practice
1. 过度使用委托
// ❌ 错误:过度使用委托,使代码难以理解
class OveruseExample {
var property1: String by SomeDelegate()
var property2: Int by AnotherDelegate()
var property3: Boolean by YetAnotherDelegate()
// ... 更多委托
}
// ✅ 正确:只在必要时使用委托
class BetterExample {
var simpleProperty: String = ""
var complexProperty: Int by CachingDelegate() // 只对复杂逻辑使用委托
}2. 委托链过长
// ❌ 错误:委托链过长,增加复杂性
class DeepDelegate1 {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String = "1"
}
class DeepDelegate2(delegate: DeepDelegate1) {
private val delegate = delegate
operator fun getValue(thisRef: Any?, property: KProperty<*>): String = "2: ${delegate.getValue(thisRef, property)}"
}
class DeepDelegate3(delegate: DeepDelegate2) {
private val delegate = delegate
operator fun getValue(thisRef: Any?, property: KProperty<*>): String = "3: ${delegate.getValue(thisRef, property)}"
}
class Example {
val value: String by DeepDelegate3(DeepDelegate2(DeepDelegate1()))
}
// ✅ 正确:保持委托链简洁
class SimpleDelegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String = "Value"
}
class BetterExample {
val value: String by SimpleDelegate()
}3. 委托中的副作用
// ❌ 错误:委托的 getValue 方法有副作用
class BadDelegate {
private var count = 0
operator fun getValue(thisRef: Any?, property: KProperty<*>): Int {
count++ // 副作用:每次访问都会增加计数
return count
}
}
class Example {
val value: Int by BadDelegate()
}
// ✅ 正确:委托方法应该是无副作用的
class GoodDelegate {
private val value = 42
operator fun getValue(thisRef: Any?, property: KProperty<*>): Int {
return value // 无副作用
}
}4. 委托与线程安全
// ❌ 错误:委托不是线程安全的
class UnsafeDelegate {
private var value: Int = 0
operator fun getValue(thisRef: Any?, property: KProperty<*>): Int = value
operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: Int) {
value = newValue // 非原子操作
}
}
// ✅ 正确:使用线程安全的委托
class SafeDelegate {
@Volatile private var value: Int = 0
operator fun getValue(thisRef: Any?, property: KProperty<*>): Int = value
operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: Int) {
value = newValue
}
}
// 或者使用线程安全的 lazy
val safeValue: Int by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
computeExpensiveValue()
}5. 委托的性能问题
// ❌ 错误:委托方法执行耗时操作
class SlowDelegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
Thread.sleep(100) // 耗时操作
return "Value"
}
}
class Example {
val value: String by SlowDelegate()
}
// ✅ 正确:使用缓存避免重复计算
class CachingDelegate {
private val value by lazy {
Thread.sleep(100) // 只执行一次
"Value"
}
operator fun getValue(thisRef: Any?, property: KProperty<*>): String = value
}最佳实践
1. 明确委托的职责
// ✅ 好的做法:委托职责明确
class LoggingDelegate<T>(private var value: T) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
println("获取 ${property.name}")
return value
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: T) {
println("设置 ${property.name} = $newValue")
value = newValue
}
}
class User {
var name: String by LoggingDelegate("")
var age: Int by LoggingDelegate(0)
}2. 使用扩展函数简化委托创建
// ✅ 好的做法:使用扩展函数
fun <T> logged(initialValue: T) = LoggingDelegate(initialValue)
class User {
var name: String by logged("")
var age: Int by logged(0)
}3. 组合多个委托的功能
// ✅ 好的做法:组合委托功能
class ValidatingLoggingDelegate<T>(
private var value: T,
private val validator: (T) -> Boolean
) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
println("获取 ${property.name}: $value")
return value
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: T) {
if (validator(newValue)) {
println("设置 ${property.name} = $newValue")
value = newValue
} else {
println("拒绝设置 ${property.name} = $newValue")
}
}
}
fun <T> validatedLogged(initialValue: T, validator: (T) -> Boolean) =
ValidatingLoggingDelegate(initialValue, validator)
class User {
var email: String by validatedLogged("", { it.contains("@") })
var age: Int by validatedLogged(0, { it >= 0 })
}4. 为委托添加文档
/**
* 缓存委托,用于延迟加载和缓存值
* @param loader 加载值的函数
*/
class CachingDelegate<T>(private val loader: () -> T) {
private var cachedValue: T? = null
/**
* 获取值,如果未缓存则调用 loader 加载
*/
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return cachedValue ?: loader().also { cachedValue = it }
}
}
/**
* 创建缓存委托
* @param loader 加载值的函数
*/
fun <T> cached(loader: () -> T) = CachingDelegate(loader)5. 合理使用标准委托
// ✅ 好的做法:根据场景选择合适的标准委托
class Example {
// 延迟初始化,适合计算密集型操作
val expensiveValue: String by lazy {
computeExpensiveValue()
}
// 监听属性变化
var name: String by Delegates.observable("") {
_, old, new ->
println("名字从 $old 变为 $new")
}
// 验证属性值
var age: Int by Delegates.vetoable(0) {
_, _, new ->
new >= 0
}
// 从 Map 中读取属性
constructor(properties: Map<String, Any?>) {
val name by properties
val age by properties
// ...
}
}6. 避免在委托中使用 this
// ❌ 错误:在委托中使用 this 可能导致循环引用
class BadDelegate(val owner: Example) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return owner.someMethod() // 可能导致循环引用
}
}
class Example {
val value: String by BadDelegate(this) // 构造时传递 this
fun someMethod(): String {
return "Value"
}
}
// ✅ 正确:使用弱引用或避免直接引用
class GoodDelegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return (thisRef as? Example)?.someMethod() ?: ""
}
}
class Example {
val value: String by GoodDelegate()
fun someMethod(): String {
return "Value"
}
}常见问题与解决方案
1. 委托属性的初始化顺序
// 问题:委托属性的初始化顺序可能影响其他属性
class Example {
val dependency by lazy { "依赖值" }
val dependent by lazy { "依赖于: $dependency" }
}
// 解决方案:确保依赖属性先被初始化
fun main() {
val ex = Example()
println(ex.dependency) // 先初始化依赖
println(ex.dependent) // 然后使用依赖
}2. 委托与继承
// 问题:父类的委托属性在子类中可能被覆盖
open class Parent {
open val value: String by lazy { "Parent" }
}
class Child : Parent() {
override val value: String by lazy { "Child" }
}
// 解决方案:使用 open/override 正确处理
fun main() {
val child = Child()
println(child.value) // 输出: Child
}3. 委托与序列化
// 问题:委托属性可能不会被自动序列化
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable
class User {
var name: String by Delegates.observable("") { _, _, _ -> }
// 序列化可能有问题
}
// 解决方案:使用 @Transient 或自定义序列化
@Serializable
class BetterUser {
@Transient
private val delegate = Delegates.observable("") { _, _, _ -> }
var name: String by delegate
}4. 委托的内存泄漏
// 问题:委托可能持有外部对象的引用导致内存泄漏
class LeakyDelegate(private val callback: () -> Unit) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "Value"
}
}
class Example {
val value: String by LeakyDelegate { println("Callback") }
// 如果 callback 引用了外部对象,可能导致内存泄漏
}
// 解决方案:使用弱引用
class SafeDelegate(private val callback: () -> Unit) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "Value"
}
// 确保回调不会导致内存泄漏
}5. 委托的调试困难
// 问题:委托可能使调试变得困难
class ComplexDelegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
// 复杂逻辑
return "Value"
}
}
class Example {
val value: String by ComplexDelegate()
}
// 解决方案:添加日志和断点
class DebugDelegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
println("[DEBUG] 获取 ${property.name}")
// 可以添加断点
return "Value"
}
}总结
Kotlin 的委托功能是一种强大的语言特性,它提供了:
- 类委托:通过
by关键字将接口实现委托给另一个对象,实现组合优于继承的设计原则 - 属性委托:将属性的访问逻辑委托给专门的对象,简化代码并提高可维护性
- 标准委托:提供了
lazy、observable、vetoable、map等常用委托实现 - 自定义委托:允许开发者根据具体需求创建自定义的委托实现
- 进阶用法:支持委托链、反射、协程等高级场景
最佳实践:
- 只在必要时使用委托,避免过度使用
- 保持委托链简洁,避免过度复杂化
- 确保委托方法无副作用,提高代码可预测性
- 注意线程安全问题,特别是在多线程环境中
- 为委托添加适当的文档和注释
- 合理使用标准委托,根据场景选择合适的实现
通过掌握 Kotlin 的委托功能,你可以编写更加优雅、灵活和可维护的代码,充分发挥 Kotlin 语言的优势。