Kotlin 的类委托及委托属性详解
类委托 | 属性委托 |
---|---|
by | 延迟属性(lazy) |
- | 可观测属性(Observable) |
- | 非空属性(Delegate.notnull) |
- | map属性(Map<String, Any?>) |
- | 提供委托(provideDelegate ) |
类委托
class MyDelegation{
operator fun getValue(thisRef:Any?,protery:KProperty<*>):String="$thisRef,your delegated protery name is ${protery.name}"
operator fun setValue(thisRef:Any?,protery:KProperty<*>,value:String)= println("$thisRef,new value is $value")
}
class MyProteryClass{
var name:String by MyDelegation() //属性申明必须初始化,这里把当前属性委托给了MyDelegation类。
}
fun main(args: Array<String>) {
val myproteryClass=MyProteryClass()
myproteryClass.name="Hello world" //这是设置value
println(myproteryClass.name) //这是获取value
}
类委托原理:by关键字后面的对象实际上被储存在类的内部,编译器则会父接口中的所有方法实现出来,并且将实现转移给委托对象去执行。 by后面的就是委托给当前对象。
属性委托
延迟属性(lazy):指的是属性只在第一次被访问的时候才会计算,之后则会将之前计算的结果缓存起来供后续调用。
lazy后有三种情况:
- SYNCHRONIZED: 默认情况下延迟属性是同步的,值只会在一个线程中得到计算,所有线程都会使用相同的结果。
- PUBLICATION:如果不需要初始化委托的同步,这样多个线程可以同时执行。
- NONE:如果确定初始化操作只在一个线程中执行,这样会减少线程安全方面的开销。
val myLazyValue:Int by lazy(LazyThreadSafetyMode.NONE){
println("hello world")
30
}
fun main(args: Array<String>) {
println(myLazyValue)
}
非空属性(notNull):notNull 适用于那些在初始化无法确定属性值的场合。
class MyPerson{
var address:String by Delegates.notNull<String>() //notNull
}
fun main(args: Array<String>) {
val myPerson= MyPerson()
myPerson.address="chongqing"
println(myPerson.address)
}
可观测属性(Observable):
Delegates.Observable 接收2个参数:初始值与修改处理器。处理器会在我们每次赋值时得到调用(在赋值完成后被调用)。处理器本身接收3个参数:被赋值的属性本身,旧的属性值与新的属性值。
Delegates.vetoable 调用时机与Delegates.Observable相反,它是在对属性赋值之前被调用,根据Delegates.vetoable返回的 结果是true或false来决定是否真正对属性赋值。
class Person{
var age:Int by Delegates.Observable(20){
prop,oldvalue,newvalue->println("${prop.name},oldvalue:$oldvalue,newvalue:$newvalue")
}
}
class Person2{
var age:Int by Delegates.vetoable(20){
prop,oldvalue,newvalue->when{
oldvalue<=newvalue->true
else ->false
}
}
}
fun main(args: Array<String>) {
val myPerson= Person()
myPerson.age=30 //得到结果:age,oldvalue:20,newvalue:30
myPerson.age=40 //得到结果:age,oldvalue:30,newvalue:40
println("----------")
val myPerson2= Person2()
println(myPerson2.age) //得到结果:20
myPerson2.age=40
println(myPerson2.age) //得到结果:40,因为20<40得到true,赋值成功。
println("----------")
myPerson2.age=30
println(myPerson2.age) //因为40>30得到false,赋值失败。新值是40了。
}
map属性(Map):
将属性储存到map中,一种常见的应用场景是将属性值储存到map中。这通常出现在json解析或是一种动态行为。在这种情况中,你可以将map实例作为委托,作为类中的属性的委托。map中的key名字要与类中属性名字一致。
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
}
class Student2(map: MutableMap<String, Any?>) { //可变的map
val address: String by map
}
fun main(args: Array<String>) {
val student = Student(
mapof(
"name" to "zhangsan",
"address" to "chengdu", //必须用to
"age" to20,
"birthday" to Date()
)
)
println(student.name)
println(student.address)
println(student.age)
println(student.birthday)
val map=MutableMapOf("address" to "chongqing")
val student2 = Student2(map) //此时把map放进来会报Reuqerd:String,Any? found:String,String
//修改上面报错的问题,定义返回的类型
val map2:MutableMap<String, Any?>=MutableMapOf("address" to "chongqing")
val student2 = Student2(map2) 就不会报错了
println(map2["address"]) //chongqing
println(student2.address)//chongqing
println("------------")
student2.address="chengdu"
println(map2["address"])//chengdu
println(student2.address)//chengdu
}
提供委托(providing a delegate)
通过定义provideDelegate operator,我们可以扩展委托的创建逻辑过程。如果对象定义了provideDelegate方法,那么该方法就会被调用来创建属性委托实例。
class PropertyDelegate:ReadOnlyProperty<People,String>{
override fun getValue(thisRef:People,property:KProperty<*>):String{
return "hello world"
}
}
class PeopleLauncher{
operator fun provideDelegate(thisRef:People,property:KProperty<*>):ReadOnlyProperty<People,String>{
when(property.name){
"name","address"->return PropertyDelegate()
else ->throw Exception("not valid name")
}
}
}
class People{
val name:String by PeopleLauncher()
val address:String by PeopleLauncher()
}
fun main(args: Array<String>) {
val people=People()
println(people.name) //输出 hello world
println(people.address) //输出 hello world
println(people.age) // "not valid name" 因为属性名称不一致
}
属性委托的要求
对于只读属性来说(val修饰的属性),委托需要提供一个名为getvalue的方法,该方法接收如下参数:
-thisRef: 需要是属性拥有者相同的类型或是父类型(对于扩展属性来说,这个类型指的是被扩展的那个类型)
-property: 需要时KProperty<*>类型或是其父类型
getValue方法需要返回与属性相同的类型或是其子类型
对于可变属性来说(var修饰的属性),委托需要再提高一个setvalue方法,该方法需要接收如下参数:
-thisRef: 与getvalue方法thisRef要求一致。
-property: 与getvalue方法property要求一致。
-new value:需要与属性的类型相同或是其父类型。
getValue与setvalue方法既可以作为委托类的成员方法实现,也可以作为其扩展方法来实现。
这2个方法都必须要标记为operator关键字,对于委托类来说,它可以实现ReadOnlyProperty或是ReadWriteProperty接口,
这些接口包含了相应的getvalue与setvalue方法,对于委托类来说,也可以不去实现这2个接口,而是自己单独实现相应的
getvalue与setvalue方法。
委托转换规则:
对于每个委托属性来说,Kotlin编译器在底层会生成一个辅助的属性,然后将对原有属性的访问委托给这个辅助属性。比如
说,对于属性prop来说,Kotlin编译器所生成的隐含的属性名为prop$delegate的属性,然后对原有的prop属性的
访问器的访问都只是委托给了这个额外的,Kotlin编译器所生成的辅助属性。