什么是委托?
就是我让A去做一件事情,A自己不去做,而是把这个事情委托给B去完成,B是A的委托。这个是委托的基本概念。
类委托
interface MyInterface {
fun myPrint()
}
实现这个接口
class MyInterfaceImpl(val str: String) : MyInterface {
override fun myPrint() {
println("welcome" + str)
}
}
接下来看下委托的基本实现方式
MyClass不打算自己去实现MyInterface的 myPrint()方法,而是委托给MyInterfaceImpl这个类的实例。实现方法如下:
class MyClass (myInterface: MyInterface):MyInterface by myInterface{
}
MyClss里面的 myPrint()的实现整个的委托给了构造方法的参数myInterface去做。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main12)
val myInterfaceImpl = MyInterfaceImpl("张三")
myInterfaceImpl.myPrint()
val myClass = MyClass(myInterfaceImpl).myPrint()
}
委托原理:by关键字后面对象实际上会被存储在类的内部,编译器则会将父接口中的所有方法实现出来,并且将实现转移给委托对象去进行。
也就是by后面的myInterface会被存储在MyClass 的内部,编辑器是会把父接口中的所有方法实现出来,实现的逻辑就是直接调用委托的实现方法。
委托属性
如果在一个类里面定义了一个属性,我们可以给属性赋值,也可以获取到这个值,相当于set()/get(),已经由Kotlin编译器生成了。如果采用了委托属性的概念的话,被委托的属性的set()和get()不是由Kotlin编译器生成,而是转移给委托的那一方。
class MyDelegate {
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.")
}
}
格式有严格要求,必须叫getValue(),setValue()
getValue():第一个参数是对象、第二个参数是属性
setValue():第一个参数是对象、第二个参数是属性,第三个参数是值
class MyPropertyClass {
var str: String by MyDelegate()
}
class MyPropertyClass {
var str: String by MyDelegate()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main13)
val myPropertyClass = MyPropertyClass()
myPropertyClass.str = "hello world"//调用set方法
println(myPropertyClass.str)//调用get方法
}
委托属性有四种情况在开发中比较有用:
1.延迟属性 Lazy
第一次访问的时候会执行一个计算,当你第二次访问的时候就不计算了,而是把第一次计算的结果缓存起来,每一次直接获取结果。
2.可观察属性 Observable
当你给一个属性赋值的时候,这个属性相当于有一个监听器一样,在赋值之前和赋值之后,监听器会收到通知,执行一些预先处理或者事后处理。
3.非空属性
属性不能为空
4.map属性
属性名和属性值,相当于key和value。类中的所有属性委托给一个map对象统一的管理,而不是每个存在单独的字段中。比如支付宝的支付,当你在用户支付完以后,支付宝的服务器会给你自己的服务器返回一个异步的通知,支付宝的参数是动态的,请求参数的结构是动态的,我们就可以声明一个map。
接下来举例说明下
延迟属性 Lazy
val myLazyValue :Int by lazy {
println("hello world")
30
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main14)
println(myLazyValue)
println(myLazyValue)
}
第一次访问的时候会执行lamda表达式,当第二次访问的时候,值已经被计算过一次,30作为lamda表达式的值,不会再去执行lamda表达式。
可观察属性 Observable
class Person {
var age: Int by Delegates.observable(20) { property, oldValue, newValue ->
println("${property.name},oldValue:$oldValue,newValue:$newValue")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main15)
val person = Person()
person.age = 30
person.age = 40
println("----------")
}
class Person2 {
var age: Int by Delegates.vetoable(20) { property, oldValue, newValue ->
when {
oldValue <= newValue -> true
else -> false
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main15)
val person2 = Person2()
println("${person2.age}")
println("----------")
person2.age = 40
println("${person2.age}")
println("----------")
person2.age =30
println("${person2.age}")
}
Delegates.observable接收2个参数分别是初始值和修改处理器
处理器会在我们每次对属性赋值时得到调用,在赋值之后调用。
处理器本身接收3个参数分别是被赋值的属性本身,旧的属性值和新的属性值
Delegates.vetoable的调用时机和Delegates.observable恰好相反,它是在属性被赋值之前被调用
根据Delegates.vetoable的返回值是true还是false来决定真正对属性进行赋值
map委托
将属性存储到map中。
一种常见的应用场景是出现在Json解析或者是一些动态行为。
在这种情况中,你可以将map实例作为委托,作为类中属性的委托。
只读属性的委托
class Student(map: Map<String, Any?>) {
val name: String by map
val address: String by map
val age: Int by map
val birthday: Date by map
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main15)
val student = Student(
mapOf(
"name" to "zhangsan",
"address" to "hangzhou",
"age" to 20,
"birthday" to Date()
))
println(student.name)
println(student.address)
println(student.age)
println(student.birthday)
}
map中的key的名字要和属性的名字要一致
读写属性的委托
class Student2(map: MutableMap<String, Any?>) {
var address:String by map
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main15)
val map: MutableMap<String, Any?> = mutableMapOf(
"address" to "beijing"
)
val student2 = Student2(map)
println(student2.address)
println(map["address"])
student2.address = "shanghai"
println(student2.address)
println(map["address"])
}
非空属性
适用于没有办法初始化阶段确定初始值的场合
class MyPerson {
var address: String by Delegates.notNull<String>()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main14)
val myPerson = MyPerson()
myPerson.address = "hangzhou"
}
关于属性委托的要求
对于只读属性来说(val 修饰的属性),委托需要提供一个getValue的方法,该方法接收如下参数:
- thisRef:需要是属性拥有这或者是其父类型
- property:需要是KProperty<*>类型或者是其父类型
getValue方法需要返回与其属性相同的类型或者其子类型
对于读写属性来说(var修饰的属性),委托需要提供一个名为setValue的方法,该方法需要接收如下参数:
-thisRef,与getValue方法的thisRef要求一致
-property,与property方法的thisRef要求一致
-new value,需要与属性的类型相同或者是其父类型
getValue和setValue方法既可以作为委托类的成员方法来实现,也可以作为其扩展方法来实现这俩个方法都必须要标记为operator关键字,对于委托类来说,它可以实现ReadOnlyProperty 或者ReadWriteProperty 接口,包含了响应的getValue和setValue方法。