前言
一、类的委托
背景:委托模式是类继承模式之外的另一种很好的替代方案,kotlin直接支持委托模式。
定义:类的委托即一个类中定义的方法实际是调用另一个类的对象的方法来实现的。
interface CanFly{
fun fly()
}
class Bird(f:CanFly):CanFly by f // 只有接口可以委托哦!编译器会生成继承自CanFly接口的所有方法,并将调用转发给f对象
class AnimalWithWings :CanFly{
override fun fly() {
println("use wings to fly")
}
}
class Bat:CanFly by AnimalWithWings() //编译器会生成继承自CanFly接口的所有方法,并将调用转发给AnimalWithWings对象
fun main(){
Bird(AnimalWithWings()).fly()
Bat().fly()
}
二、委托属性
背景:具有共性的属性,在每个需要这些属性的类中手工实现比较麻烦,如果能够只实现一次,供所有需要的类使用,那将会好很多。
定义:一个类的某个属性值不在类中直接进行定义,而是将其托付给一个代理类,从而实现对该类的属性统一管理
语法:val/var <属性名>: <类型> by <表达式>
官网上的自定义委托实现方式是这样的,
class User {
var c: String by C()
}
class C {
operator fun setValue(thisRef: Any, property: KProperty<*>, value: String) {
println("$thisRef 的${property.name}属性赋值为$value")
}
operator fun getValue(thisRef: Any, property: KProperty<*>): String {
return "$thisRef,委托了${property.name}属性"
}
}
更快捷的实现方式如下:
class User {
var c: String by C()
}
class C : ReadWriteProperty<Any, String> {
override fun setValue(thisRef: Any, property: KProperty<*>, value: String) {
println("${thisRef}的${property.name}属性赋值为$value")
}
override fun getValue(thisRef: Any, property: KProperty<*>): String {
return "$thisRef,委托了${property.name}属性"
}
}
这里看一下ReadWriteProperty类的内容:
public interface ReadWriteProperty<in R, T> {
public abstract operator fun getValue(thisRef: R, property: kotlin.reflect.KProperty<*>): T
public abstract operator fun setValue(thisRef: R, property: kotlin.reflect.KProperty<*>, value: T): kotlin.Unit
}
输出
com.example.demo.User@9bdf2f3的c属性赋值为c
com.example.demo.User@9bdf2f3,委托了c属性
三、标准委托
Kotlin 标准库中提供了一些工厂方法, 可以实现几种很有用的委托
1. 延迟加载
lazy()是一个函数,接受一个lambda表达式作为参数,返回一个Lazy<T>类型的实例,这个实例可以作为一个委托,实现延迟加载属性:第一次调用 get() 时, 将会执行 lazy() 函数受到的 Lambda 表达式, 然后会记住这次执行的结果, 以后所有对 get() 的调用都只会简单地返回以前记住的结果,
val l by lazy {
println("computed")
"lazy"
}
var user = User()
println(user.l) // 输出computed lazy
println(user.l) //输出lazy
2. 可观察属性
有了这个,再也不用思考怎么实时监控属性变化了\(^o^)/~
- Delegates.observable()
Delegates.observable() 函数接受两个参数:第一个是初始化值,第二个是属性值变化事件的响应器 (handler). 每次我们向属性赋值时, 响应器(handler)都会被调用(在属性赋值处理完成 之后). 响应器收到三 个参数: 被赋值的属性, 赋值前的旧属性值, 以及赋值后的新属性值
class User {
var o by Delegates.observable("<none>") {
kProperty: KProperty<*>, old: String, new: String ->
println("${kProperty.name}: $old -> $new")
}
}
var user = User()
println(user.o) // 输出<none>
user.o = "first change" // 输出o: <none> -> first change
println(user.o) // first change
user.o = "second change" // 输出 o: first change -> second change
println(user.o) // 输出second change
- Delegates.vetoable()
如果你希望能够拦截属性的赋值操作, 并且还能够 “否决” 赋值操作, 那么不要使用 observable() 函数, 而应该改用 vetoable() 函数. 传递给 vetoable 函数的事件响应器, 会在属性赋值处理执行之前被调用.
var v by Delegates.vetoable("<none>") {
kProperty: KProperty<*>, old: String, new: String ->
println("${kProperty.name}: $old -> $new")
new.contains("change")
}
var user = User()
println(user.v) //输出<none>
user.v = "first change" //输出v: <none> -> first change
println(user.v) //输出first change
user.v = "second " //输出v: first change -> second
println(user.v) //输出first change
user.v = "third change" //输出v: first change -> third change
println(user.v) //输出third change
- Delegates.notNull
如果这个值在被获取之前没有被分配,它就会抛出 一个异常。
class User {
var o by Delegates.notNull<String>()
}
user.o = "ooo"
println(user.o) //报错,Caused by: java.lang.IllegalStateException: Property o should be initialized before get.
3. 将多个属性保存在一个map内
使用 map 实例本身作为属性的委托
class User(map: MutableMap<String, Any?>) {
var id: Long by map
var name: String by map
}
class Teacher(map: MutableMap<String, Any?>) {
var id: Long by map
var name: String by map
}
var map: MutableMap<String, Any?> = mutableMapOf("name" to "user", "id" to 1)
var user = User(map)
var t = Teacher(map)
println("before change")
println("user ->${user.id}--${user.name}")
println("teacher->${t.id}--${t.name}")
println(" change map id to 850")
map.set("id", 850)
println("user ->${user.id}--${user.name}")
println("teacher->${t.id}--${t.name}")
println(" change user.name to userchange")
user.name = "userchange"
println("user ->${user.id}--${user.name}")
println("teacher->${t.id}--${t.name}")
println("map->${map.entries}")