前言
kotlin官網(wǎng)和kotlin教程學(xué)習(xí)教程的筆記。
一、類的委托
背景:委托模式是類繼承模式之外的另一種很好的替代方案,kotlin直接支持委托模式。
定義:類的委托即一個(gè)類中定義的方法實(shí)際是調(diào)用另一個(gè)類的對(duì)象的方法來實(shí)現(xiàn)的。
interface CanFly{
fun fly()
}
class Bird(f:CanFly):CanFly by f // 只有接口可以委托哦!編譯器會(huì)生成繼承自CanFly接口的所有方法,并將調(diào)用轉(zhuǎn)發(fā)給f對(duì)象
class AnimalWithWings :CanFly{
override fun fly() {
println("use wings to fly")
}
}
class Bat:CanFly by AnimalWithWings() //編譯器會(huì)生成繼承自CanFly接口的所有方法,并將調(diào)用轉(zhuǎn)發(fā)給AnimalWithWings對(duì)象
fun main(){
Bird(AnimalWithWings()).fly()
Bat().fly()
}
二、委托屬性
背景:具有共性的屬性,在每個(gè)需要這些屬性的類中手工實(shí)現(xiàn)比較麻煩,如果能夠只實(shí)現(xiàn)一次,供所有需要的類使用,那將會(huì)好很多。
定義:一個(gè)類的某個(gè)屬性值不在類中直接進(jìn)行定義,而是將其托付給一個(gè)代理類,從而實(shí)現(xiàn)對(duì)該類的屬性統(tǒng)一管理
語法:val/var <屬性名>: <類型> by <表達(dá)式>
官網(wǎng)上的自定義委托實(shí)現(xiàn)方式是這樣的,
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}屬性"
}
}
更快捷的實(shí)現(xiàn)方式如下:
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類的內(nèi)容:
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屬性
三、標(biāo)準(zhǔn)委托
Kotlin 標(biāo)準(zhǔn)庫中提供了一些工廠方法, 可以實(shí)現(xiàn)幾種很有用的委托
1. 延遲加載
lazy()是一個(gè)函數(shù),接受一個(gè)lambda表達(dá)式作為參數(shù),返回一個(gè)Lazy<T>類型的實(shí)例,這個(gè)實(shí)例可以作為一個(gè)委托,實(shí)現(xiàn)延遲加載屬性:第一次調(diào)用 get() 時(shí), 將會(huì)執(zhí)行 lazy() 函數(shù)受到的 Lambda 表達(dá)式, 然后會(huì)記住這次執(zhí)行的結(jié)果, 以后所有對(duì) get() 的調(diào)用都只會(huì)簡(jiǎn)單地返回以前記住的結(jié)果,
val l by lazy {
println("computed")
"lazy"
}
var user = User()
println(user.l) // 輸出computed lazy
println(user.l) //輸出lazy
2. 可觀察屬性
有了這個(gè),再也不用思考怎么實(shí)時(shí)監(jiān)控屬性變化了\(^o^)/~
- Delegates.observable()
Delegates.observable() 函數(shù)接受兩個(gè)參數(shù):第一個(gè)是初始化值,第二個(gè)是屬性值變化事件的響應(yīng)器 (handler). 每次我們向?qū)傩再x值時(shí), 響應(yīng)器(handler)都會(huì)被調(diào)用(在屬性賦值處理完成 之后). 響應(yīng)器收到三 個(gè)參數(shù): 被賦值的屬性, 賦值前的舊屬性值, 以及賦值后的新屬性值
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() 函數(shù), 而應(yīng)該改用 vetoable() 函數(shù). 傳遞給 vetoable 函數(shù)的事件響應(yīng)器, 會(huì)在屬性賦值處理執(zhí)行之前被調(diào)用.
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
如果這個(gè)值在被獲取之前沒有被分配,它就會(huì)拋出 一個(gè)異常。
class User {
var o by Delegates.notNull<String>()
}
user.o = "ooo"
println(user.o) //報(bào)錯(cuò),Caused by: java.lang.IllegalStateException: Property o should be initialized before get.
3. 將多個(gè)屬性保存在一個(gè)map內(nèi)
使用 map 實(shí)例本身作為屬性的委托
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}")
