- 在线测试kotlin代码: https://play.kotlinlang.org/
1. 委托模式与mixin
委托模式
即是委托类将某种处理/请求全权交给被委托类处理,自身只是包含被委托类的实例引用,或者实现被委托类的接口方法。调用者看来是委托类在完成请求,而实际工作的则是被委托类。
这么做除了满足具体的业务需求外,也符合设计模式中用组合代替继承
的原则,解耦了类之间is-a的继承关系。之前学习Dart
时遇到的mixin
关键字就是体现了这种原则。
- Dart: mixin
|
|
在上述例子中,Worker并没有继承Delegate类,却通过with关键字获得了使用Delegate类方法的权利。其他各种懒惰的类都可以通过同样的方法请Delegate类替自己干活,而不必成为Delegate的一员,降低了代码重用的门槛。
2. Kotlin中的by
关键字
Java中实现委托模式需要引用实例·继承接口等一顿操作,kotlin则省去了样板代码,用by
对委托实现原生支持。
根据官方文档,by关键字具有两种用法
- 委托接口的实现
- 委托属性的读写
2-1. 委托接口的实现
- 语法
1 2 3 4 5 6
class Worker(impl: DelegateInterface): DelegateInterface by impl{} //Call fun main() { val impl = DelegateInterfaceImpl() Worker(impl).methodA() //the method is implemented by impl but not the worker itself. }
- 使用场景
实现接口需要实现接口中所有的方法,哪怕并非全部必要。应用委托,我们可以先实现一个拥有所有接口方法的委托类,再在当前类中选择性地重写需要的方法。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
interface DelegateInterface { fun methodA() fun methodB() } class DelegateInterfaceImpl: DelegateInterface { override fun methodA() {} override fun methodB() {} } class WorkerA(impl: DelegateInterface): DelegateInterface by impl{ override fun methodA() {} //only need to implement what you need } class WorkerB(impl: DelegateInterface): DelegateInterface by impl{ override fun methodB() {} //only need to implement what you need } //Call fun main() { val impl = DelegateInterfaceImpl() WorkerA(impl).methodA() //only overridden method will be called WorkerB(impl).methodB() //only overridden method will be called }
2-2. 委托属性的读写(以委托Lazy
实例为例)
-
语法
1
val/var <属性名>: <类型> by <表达式>
属性委托本质上是把
get( )
/set( )
方法委托给委托类的getValue()
/setValue()
方法。对于var
(可变)属性,委托类必须提供setValue
方法,不然会报错。 -
标准委托
kotlin标准库提供了生产Lazy
,Observable
等特殊对象的工厂方法。生产出的对象可以被委托。 下面的例子描述了委托给自定义的类,和调用标准委托by lazy
的用法:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
import kotlin.reflect.KProperty class test() { //can be var or val; if var, should provide setValue method var deleProperty: String by Delegate() val lazyProperty: String? by lazy(LazyThreadSafetyMode.NONE){ println("executed only once") "lazy initialized" } } 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.") } } fun main() { val test = test() println("1. " + test.deleProperty) //1. test@7ef20235, thank you for delegating 'deleProperty' to me! println("2. " + test.lazyProperty) //executed only once ¥n 2. lazy initialized println("3. " + test.lazyProperty) //3. lazy initialized test.deleProperty = "new value" // new value has been assigned to 'deleProperty' in test@7ef20235. println("4. " + test.deleProperty) //4. test@7ef20235, thank you for delegating 'deleProperty' to me! }
console output
1. test@7ef20235, thank you for delegating 'deleProperty' to me! executed only once 2. lazy initialized 3. lazy initialized new value has been assigned to 'deleProperty' in test@7ef20235. 4. test@7ef20235, thank you for delegating 'deleProperty' to me!
-
原理解析
by lazy
中的lazy
实际是一个生产Lazy<T>
对象的工厂方法。它的源码如下:1
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
该方法接收lambda表达式,调用
SynchronizedLazyImpl
方法后,返回Lazy<T>
对象,这个Lazy<T>
就是被委托的对象。而SynchronizedLazyImpl
的执行是同步锁的。如果不需要同步锁,可以传入LazyThreadSafetyMode
线程模式来实现不同的初始化模式。 传入线程模式时的方法源码:1 2 3 4 5 6
public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> = when (mode) { LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer) LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer) LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer) }
不论哪种初始化模式,在XXXLazyImpl方法中都实现了
Lazy
接口,并重写了其中value
属性的get
方法,也就是提供了新的getValue
方法。 该方法规定:- value未被初始化时,执行lambda表达式实施初始化
- value已被初始化,直接返回值
-
应用场景
除了lazy以外,安卓标准库还有实现了Lazy接口的其他方法,比如可以像这样把viewModel属性委托给Fragment.viewModels
方法生产的对象:1
val viewModel by viewModels<MyViewModel> { myFactoryProvider }
这个看起来有点难理解,其实和上面lazy的原理是一样的。
viewModels
方法返回Lazy<VM>
对象,该对象实现Lazy接口。这里的lambda表达式主要内容就是获得生产viewModel的工厂的返回值,用它来延迟初始化viewModel。 其中的myFactoryProvider
代表的是参数factoryProducer: (() -> Factory)
(kotlin中lambda表达式是最后一个参数的时候可以写到参数括号的外面),这个参数就是用来生产viewModel的工厂本厂啦。
3. 总结
- kotlin中的委托也是委托模式的一种实现,语言提供的by关键字使得委托实现更为简便。
- 通过
by
实现的委托主要有两种:1.委托接口的实现 2.委托属性的读写。 - 委托接口实现可以减少实现类对接口方法的不必要的实现。
- 委托属性读写提供
getValue/setValue
方法来替代属性原有的get/set
方法。 标准库的lazy方法实现了Lazy接口
。它提供的getValue方法使得只有第一次调用属性get方法时执行初始化代码块,后续调用则直接返回已初始化的值。