一句话总结
委托就是「甩锅侠模式」—— 自己不想干的活,交给别人干,但对外看起来像是自己干的。
一、委托的本质:编译器的魔法
在 Kotlin 中,委托是一种设计模式的语言级支持。当你使用 by 关键字时,Kotlin 编译器会自动生成所有必要的代码,将接口的实现或属性的读写逻辑代理给另一个对象。这是一种强大的语法糖,它用一行代码替代了手动编写代理模式时的大量模板代码。
二、两大核心场景:类委托与属性委托
1. 类委托:实现接口时的“甩锅”
-
用途:当一个类需要实现一个接口时,你可以将接口的所有方法实现委托给另一个对象。这避免了继承带来的紧耦合,转而使用组合来实现功能,从而提高了代码的灵活性。
-
示例:将
开车接口的实现委托给司机类。interface Car { fun startEngine() fun honk() } class Driver : Car { override fun startEngine() { println("Driver starts the engine") } override fun honk() { println("Driver honks the horn") } } // 你(Passenger)委托司机来开车 class Passenger(private val driver: Driver) : Car by driver fun main() { val myCar = Passenger(Driver()) myCar.startEngine() // Output: Driver starts the engine }
2. 属性委托:管理属性的读写行为
- 用途:将属性的
get()和set()方法的实现委托给另一个对象。这使得开发者可以将通用的属性逻辑(如懒加载、属性变更监听、缓存)封装在可复用的委托类中。 - 实现:一个属性委托类需要实现
getValue()和setValue()这两个操作符方法。
三、内置与自定义委托:无限可能
Kotlin 提供了多种内置的属性委托,大大简化了日常开发。
-
lazy():延迟初始化。只有在第一次访问属性时,才会执行初始化代码块。- 用途:适用于资源密集型或不常用对象的初始化。
-
Delegates.observable():属性变更监听。当属性值发生变化时,会触发一个回调。- 用途:数据绑定、UI 状态更新等。
-
Delegates.vetoable():属性变更拦截。可以在属性变更前进行验证,如果验证失败,则阻止变更。- 用途:权限检查、数据有效性验证。
-
Delegates.notNull():非空延迟初始化。用于在属性声明时无法立即初始化,但保证在使用前一定会被初始化的场景。
四、委托的优势:不止是语法糖
Kotlin 的委托机制,将传统的代理模式和装饰器模式以一种更简洁、更安全的方式实现,带来了显著的好处:
- 代码复用:将通用的逻辑(如属性监听、懒加载)封装在委托类中,可以在多个地方复用,避免了代码重复。
- 降低耦合:委托使得功能实现者(如
Driver)和功能调用者(如Passenger)解耦。你可以轻松地更换底层实现,而无需修改调用者的代码。 - 提高可读性:通过
by关键字,代码的意图一目了然。例如,val value by lazy { ... }清晰地表达了这是一个延迟初始化的属性。