屬性的setter和getter方法
在Kotlin
中定義了一個類的屬性后继准,可以不用寫其setter
和getter
方法,編譯的時候編譯器會自動為你生成归敬。但是有時候我們需要對屬性進(jìn)行一下特殊操作惨寿,這時候就需要重寫setter
和getter
方法香璃,那什么時候需要重寫setter
和getter
?法噩咪。
- 重寫setter方法時機(jī):
- 外部給我值時顾彰,需要做額外的處理
- 捕獲外部給值的時機(jī)
- 重寫getter方法時機(jī):
- 外部獲取值時,需要做額外的處理
- 捕獲外部需要的時機(jī)
- 懶加載
延遲初始化lateinit
lateinit var name: String
一個變量必須有值胃碾,但是定義時不知道給什么值,可以用lateinit
修飾筋搏,后續(xù)再賦值仆百。需要注意的是在使用前必須為其賦值。上面的代碼是延遲初始化定義name變量奔脐。
委托
委托 == 代理俄周,我們在Java中學(xué)過一鐘設(shè)計模式為代理模式,在Kotlin
中可以通過by關(guān)鍵詞讓編譯器自動生成委托代碼
interface DB{
fun sava()
}
class SqlDB(): DB{
override fun sava() {
println("sava in SqlDB")
}
}
class MySqlDB(): DB{
override fun sava() {
println("sava in MySqlDB")
}
}
class OracleDB(): DB{
override fun sava() {
println("sava in OracleDB")
}
}
class CreateDBAction(db: DB): DB by db
fun main(){
CreateDBAction(SqlDB()).sava()
CreateDBAction(MySqlDB()).sava()
CreateDBAction(OracleDB()).sava()
}
/**
* sava in SqlDB
* sava in MySqlDB
* sava in OracleDB
*/
反編譯后編譯器將會生成完整的CreateDBAction
類
public final class CreateDBAction implements DB {
// $FF: synthetic field
private final DB $$delegate_0;
public CreateDBAction(@NotNull DB db) {
Intrinsics.checkNotNullParameter(db, "db");
super();
this.$$delegate_0 = db;
}
public void sava() {
this.$$delegate_0.sava();
}
}
懶加載委托
fun requestDownload(): String{
println("requestDownload run ...")
Thread.sleep(2000L) // 模擬下載延時
return "下載成功"
}
val responseData: String by lazy { requestDownload() }
// 懶加載委托
fun main(){
println("準(zhǔn)備工作中")
Thread.sleep(3000L)
println("開始請求")
println(responseData) // 如果responseData沒有值則會執(zhí)行懶加載髓迎,否則直接打印responseData的值
println(responseData)
println(respomseData)
}
/**執(zhí)行結(jié)果
* 準(zhǔn)備工作中
* 開始請求
* requestDownload run ...
* 下載成功
* 下載成功
* 下載成功
*/
懶加載只會調(diào)用一次峦朗,只有被調(diào)用的時候才會被加載,在上面代碼的含義為responseData
變量只會被初始化一次排龄,后面訪問的是就是變量的值波势。
注意:
by lazy
只能修飾val
變量,而lateinit
只能修飾var
變量。
委托屬性
委托屬性公用被委托屬性的set和get方法尺铣,底層原理為編譯器生成被委托屬性的單例對象拴曲,通過該實例可以獲取到被委托對象的set和get方法,當(dāng)調(diào)用委托屬性的set和get方法時就會通過該實例調(diào)用set和get方法凛忿,從而實現(xiàn)公用set和get效果澈灼。
用途:當(dāng)一個應(yīng)用已經(jīng)上線,里面的一個變量名需要更改店溢,但是又不希望對之前代碼進(jìn)行修改叁熔,則可以使用屬性委托,重新定義一個變量來委托之前的屬性
class Simple{
// version 1.0
var info: String = "OK"
// version 2.0
var successInfo: String by ::info
}
代碼中的 var successInfo: String by ::info
這行床牧,::info
是將 info
屬性作為委托的成員引用荣回。這意味著 successInfo
屬性的讀取和寫入操作都會被委托給 info
屬性。successInfo
屬性的讀取操作會委托給 info
屬性的 getter
方法叠赦,而寫入操作會委托給 info
屬性的 setter
方法驹马。通過這種方式,successInfo
屬性可以方便地訪問和操作 info
屬性的值除秀。
自定義委托
簡單的自定義委托(依賴類)
class Owner {
var name: String by Nicely()
}
class Nicely{
private var str: String = "Default"
operator fun getValue(owner: Owner, kProperty: KProperty<*>): String{
println("get被調(diào)用了")
return str
}
operator fun setValue(owner: Owner, kProperty: KProperty<*>, value: String){
println("set被調(diào)用了")
str = value
}
}
fun main(){
val owner = Owner()
owner.name = "Nicely"
println(owner.name)
}
/**運行結(jié)果
* set被調(diào)用了
* get被調(diào)用了
* Default
*/
不依賴類(兩種方式)
// 方式一
var result: String = "Default"
private operator fun String.getValue(
thisRef: Any?,
property: KProperty<*>
): String {
return result
}
private operator fun String.setValue(
any: Any?,
property:KProperty<*>,
value: String
) {
result = value
}
// 方式二
var result: String = "Default"
operator fun String.provideDelegate(
thisRef: Any?,
property:KProperty<*>
) = object : ReadWriteProperty<Any?, String> {
override fun getValue(thisRef: Any?, property: KProperty<*>): String {
println("test getValue value:$result")
return result
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("test setValue value:$value")
result = value
}
}
提供委托
在自定義委托類StringDelegator
的基礎(chǔ)之上創(chuàng)建一個類SimpleDelegator
實現(xiàn)動態(tài)選擇委托類糯累,SimpleDelegator
類必須實現(xiàn)provideDelegate
操作符重載,方法邏輯可以自己實現(xiàn)册踩。
kProperty
為反射獲取的成員變量泳姐,根據(jù)反射獲得的成員變量名的不同來調(diào)用不同的自定義委托對象。
注意:在方法中定義的變量無法進(jìn)行屬性委托暂吉,因為無法通過反射獲取該臨時變量胖秒。
class StringDelegator(private var str: String): ReadWriteProperty<Owner2, String>{
override fun getValue(thisRef: Owner2, property: KProperty<*>): String {
println("StringDelegator#getValue run...")
return str
}
override fun setValue(thisRef: Owner2, property: KProperty<*>, value: String) {
println("StringDelegator#setValue run...")
str = value
}
}
class SimpleDelegator {
operator fun provideDelegate(
owner2: Owner2,
kProperty: KProperty<*>
): ReadWriteProperty<Owner2, String>{
return if (kProperty.name.contains("name")){
StringDelegator("Nicely")
}else{
StringDelegator("ChongQing")
}
}
}
class Owner2{
var name: String by SimpleDelegator()
var address: String by SimpleDelegator()
}
fun main(){
val owner = Owner2()
owner.name = "Tom"
owner.address = "BeiBei"
println(owner.name)
println(owner.address)
}
/**執(zhí)行結(jié)果
* StringDelegator#setValue run...
* StringDelegator#setValue run...
* StringDelegator#getValue run...
* Tom
* StringDelegator#getValue run...
* BeiBei
*/
用途
用途一:觀察 新值 舊值
class Simple1 {
var name: String by Delegates.observable("Test") {
prop, old, new ->
println("舊值:$old -> 新值:$new")
}
}
fun main() {
val simple1 = Simple1()
simple1.name = "Update1"
simple1.name = "Update2"
}
/** Output:
* 舊值:Test -> 新值:Update1
* 舊值:Update1 -> 新值:Update2
*/
用途二:觀察 setValue 與 getValue
class Item {
var info: String by object : ReadWriteProperty<Item, String> {
override fun getValue(thisRef: Item, property: KProperty<*>): String {
println("監(jiān)聽到,你在獲取值")
return ""
}
override fun setValue(thisRef: Item, property: KProperty<*>, value: String) {
println("監(jiān)聽到慕的,你在設(shè)置值 value:$value")
}
}
}
fun main() {
val item = Item()
item.info = "Derry"
println(item.info)
}
/** Output:
* 監(jiān)聽到阎肝,你在設(shè)置值 value:Derry
* 監(jiān)聽到,你在獲取值
*/