类委托
把原本自己做的事情委托给另一个对象去做。装饰者模式和代理模式都通过委托复用了行为,Kotlin 在语言层面支持了委托。
interface Function {
fun run(): String
fun jump(): String
}
class Model1 : Function {
override fun run(): String = "run"
override fun jump(): String = "jump"
}
//装饰者模式
//重写时调用持有的实例去做
class Model2(val function: Function) : Function { //持有一个实例
override fun run() = function.run() + " fast" //对实例加强
override fun jump() = function.jump() //委托实例做
}
//Kotlin委托
//实现Function接口需要的重写,委托给持有的实例去做
class Model3(val function: Function) : Function by function{
//加强的功能还是需要自己重写
//完全委托的功能可以不写,减少了模版代码
}
val model3 = Model3(Model1())
model3.run()
属性委托
by约定:将属性的 getter/setter 交给别的对象重写 getValue()/setValue() 去实现。也就是被 var 属性委托的对象的类要重写 getValue()、setValue()方法,被 val 属性委托的对象的类要重写 getValue() 方法。
//thisRef:属性所在类的类型
//property:属性
//value:属性的类型
operator fun getValue(thisRef: Any?, property: KProperty<*>)
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String)
class Phone {
//一般是先委托再去被委托的类中重写,这样IDE能自动补全代码,手写也好去写类型
val screen: String by ScreenFactory() //val属性委托给屏幕对象
var battery: String by BatteryFactory() //var属性委托给电池对象
}
//被委托的屏幕类
class ScreenFactory {
private val str: String = "屏幕"
operator fun getValue(phone: Phone, property: KProperty<*>): String = str
}
//被委托的电池类
class BatteryFactory {
private var str = "电池"
operator fun getValue(phone: Phone, property: KProperty<*>): String = str
operator fun setValue(phone: Phone, property: KProperty<*>, s: String) { str = s }
}
val phone = Phone()
println(phone.battery) //打印:电池
phone.battery = "坏电池"
println(phone.battery) //打印:坏电池
使用场景:?
//被委托的类
class Find {
operator fun getValue(activity: Activity, kProperty: KProperty<*>): T {
println("操作的属性名是 ${property.name}")
return activity.findViewById(id)
}
}
//给Activity扩展
fun <T : View> Activity.find(id: Int) = Find<T>(id)
//使用
class KotlinActivity : AppCompatActivity() {
private val image: ImageView by find(R.id.iv)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
系统提供的一些委托 Delegate
Delegates.notNull( ):可以不在创建实例的时候初始化,而是在延迟到之后再初始化。属性委托后,编译器不会再做非空检查,不初始化也不会报错,所以需要做到自己可控(无论在类中还是类外要对属性赋值),否则在使用之前未初始化,调用会报错?IllegalStateException。
Delegates.notNull() | 适用于:基本类型、引用类型。 不支持外部注入工具将它直接注入到Java字段中。 | lateinit | 适用于:引用类型。 |
class Demo {
var num: Int by Delegates.notNull()
}
val demo = Demo()
demo.num = 3
println(demo.num)
Delegates.observable( ):类似于观察者用于监听属性值发生变更,当属性值被更改后会往外抛出一个回调。
class Demo {
//initialValue参数:初始化值
//onChange参数:是一个Lambda回调提供三个参数可用:property属性,oldValue旧值,newValue新值
var num: Int by Delegates.observable(1) { property, oldValue, newValue -> println("发生了变化:${property.name},$oldValue,$newValue") }
}
val demo = Demo()
demo.num = 3 //打印:发生了变化:num,1,3
Delegates.vetoable( ):同上,不同的是回调会返回一个 Boolean 值,来决定此次修改是否通过。
class Demo {
//新值是负数才能修改成功
var num: Int by Delegates.vetoable(1) { property, oldValue, newValue -> newValue < 0 }
}
val demo = Demo()
demo.num = 3
println(demo.num) //打印:1
demo.num = -3
println(demo.num) //打印:3
幕后属性 Backing Property
关联知识点:幕后字段 Backing Field、延迟初始化 lateinit
? ? ? ? 类似于 Java 私有化字段并提供公共访问的写法(封装),Kotlin 一般不这样写,毕竟在 getter/setter中就能对字段的操作加以限制,是用来实现懒加载的惯用技术。
????????如果一个类有两个概念上相同的属性,一个是公共 API 的一部分,另一个是实现细节,那么使用下划线作为私有属性名称的前缀。
????????幕后属性 _bitmap 是私有的,它用来存储一组 Bitmap,而另一个同样类型的 bitmap 用来提供一组 Bitmap 的访问。这样只有当第一次访问 BitmapManager.bitmaps 时,才会去加载 Bitmap。第二次访问时,也不会重新加载 Bitmap,可直接返回 _bitmap。
????????下面这段代码就是 Kotlin 提供的?lazy()?内部运用的技术。
class BitmapManager {
//幕后属性用于私有化 Bitmap
private var _bitmaps: List<Bitmap>? = null
//提供外部访问 Bitmap
val bitmaps: List<Bitmap>
get() {
if (_bitmaps == null) {
_bitmaps = loadBitmaps()
}
return _bitmaps!!
}
}
懒加载 by lazy()
- 延迟对象的初始化,直到第一次访问(使用)它。当初始化消耗大量资源时使用。
- 只能对 val 的属性使用。第一次调用 getter 执行传递的 Lambda 并记录结果,再次调用只返回之前的结果。
- 线程安全。
Lazy.kt | Lazy 接口 | public interface Lazy<out T> { ? ? public val value: T? ? ? ? //懒加载的值,一旦被赋值件将不会被改变 ? ? public fun isInitialized(): Boolean? ? ? ? //检查是否被初始化 } | by 关键字 | public inline operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value | by的实现就是把属性的getter和被委托对象的类中重写的getValue()配对,这里是Lazy的扩展函数形式。 | LazyJVM.kt | lazy() 函数 | public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer) | lazy()参数接收一个Lambda返回一个Lazy的实现类实例,有很多重载一般使用上面这个线程同步的,原理双重校验锁。 |
使用场景:所有的员工只有柜台是女性,针对少数使用到的情况选择懒加载。
//这样写在实例初始化的时候就会初始化 gender 这个属性。
class Staff(val name: String, var position: String) {
val gender: String = if (position == "Counter") "female" else "male"
}
//修改之后没有了= 赋值,只有在初次访问sex这个属性的时候,才会进行初始化。
class Staff(val name: String, var position: String) {
val gender: String by lazy {
if (position == "Counter") "female" else "male"
}
}
|