Kotlin學習 Kotlin委託

2021-10-19 14:26:23 字數 4399 閱讀 1819

委託模式是軟體設計模式中的一項基本技巧。在委託模式中,有兩個物件參與處理同乙個請求,接受請求的物件將請求委託給另乙個物件來處理。

kotlin 直接支援委託模式,更加優雅,簡潔。kotlin 通過關鍵字 by 實現委託

類的委託即乙個類中定義的方法實際是呼叫另乙個類的物件的方法來實現的。

以下例項中派生類 derived 繼承了介面 base 所有方法,並且委託乙個傳入的 base 類的物件來執行這些方法

// 建立介面

inte***ce base

// 實現此介面的被委託的類

class baseimpl(val x: int) : base

}// 通過關鍵字 by 建立委託類

class derived(b: base) : base by b

fun main(args: array)

在 derived 宣告中,by 子句表示,將 b 儲存在 derived 的物件例項內部,而且編譯器將會生成繼承自 base 介面的所有方法, 並將呼叫**給 b

屬性委託指的是乙個類的某個屬性值不是在類中直接進行定義,而是將其託付給乙個**類,從而實現對該類的屬性統一管理。

屬性委託語法格式:

val/var 《屬性名》: 《型別》 by 《表示式》
by 關鍵字之後的表示式就是委託, 屬性的 get() 方法(以及set() 方法)將被委託給這個物件的 getvalue() 和 setvalue() 方法。屬性委託不必實現任何介面, 但必須提供 getvalue() 函式(對於 var屬性,還需要 setvalue() 函式)

該類需要包含 getvalue() 方法和 setvalue() 方法,且引數 thisref 為進行委託的類的物件,prop 為進行委託的屬性的物件

import kotlin.reflect.kproperty

// 定義包含屬性委託的類

class example

// 委託的類

class delegate 屬性"

}operator fun setvalue(thisref: any?, property: kproperty<*>, value: string) 屬性賦值為 $value")

}}fun main(args: array)

輸出結果為

example@433c675d, 這裡委託了 p 屬性

example@433c675d 的 p 屬性賦值為 runoob

example@433c675d, 這裡委託了 p 屬性

kotlin 的標準庫中已經內建了很多任務廠方法來實現屬性的委託

lazy() 是乙個函式, 接受乙個 lambda 表示式作為引數, 返回乙個 lazy 例項的函式,返回的例項可以作為實現延遲屬性的委託: 第一次呼叫 get() 會執行已傳遞給 lazy() 的 lamda 表示式並記錄結果, 後續呼叫 get() 只是返回記錄的結果

val lazyvalue: string by lazy 

fun main(args: array)

執行輸出結果

computed!

hello

hello

observable 可以用於實現觀察者模式。

delegates.observable() 函式接受兩個引數: 第乙個是初始化值, 第二個是屬性值變化事件的響應器(handler)。

在屬性賦值後會執行事件的響應器(handler),它有三個引數:被賦值的屬性、舊值和新值:

import kotlin.properties.delegates

class user

}fun main(args: array)

執行輸出結果:

舊值:初始值 -> 新值:第一次賦值

舊值:第一次賦值 -> 新值:第二次賦值

乙個常見的用例是在乙個對映(map)裡儲存屬性的值。 這經常出現在像解析 json 或者做其他"動態"事情的應用中。 在這種情況下,你可以使用對映例項自身作為委託來實現委託屬性。

class site(val map: map) 

fun main(args: array)

執行輸出結果

菜鳥教程

www.runoob.com

如果使用 var 屬性,需要把 map 換成 mutablemap

class site(val map: mutablemap) 

fun main(args: array)

執行輸出結果

菜鳥教程

www.runoob.com

--------------

google

www.google.com

notnull 適用於那些無法在初始化階段就確定屬性值的場合

class foo 

foo.notnullbar = "bar"

println(foo.notnullbar)

需要注意,如果屬性在賦值前就被訪問的話則會丟擲異常

你可以將區域性變數宣告為委託屬性。 例如,你可以使乙個區域性變數惰性初始化

fun example(computefoo: () -> foo) 

}

memoizedfoo 變數只會在第一次訪問時計算。 如果 somecondition 失敗,那麼該變數根本不會計算

對於唯讀屬性(也就是說val屬性), 它的委託必須提供乙個名為getvalue()的函式。該函式接受以下引數:

這個函式必須返回與屬性相同的型別(或其子型別)。

對於乙個值可變(mutable)屬性(也就是說,var 屬性),除 getvalue()函式之外,它的委託還必須 另外再提供乙個名為setvalue()的函式, 這個函式接受以下引數:

在每個委託屬性的實現的背後,kotlin 編譯器都會生成輔助屬性並委託給它。 例如,對於屬性 prop,生成隱藏屬性 prop$delegate,而訪問器的**只是簡單地委託給這個附加屬性

class c 

// 這段是由編譯器生成的相應**:

class c

kotlin 編譯器在引數中提供了關於 prop 的所有必要資訊:第乙個引數 this 引用到外部類 c 的例項而 this::prop 是 kproperty 型別的反射物件,該物件描述 prop 自身

通過定義 providedelegate 操作符,可以擴充套件建立屬性實現所委託物件的邏輯。 如果 by 右側所使用的物件將 providedelegate 定義為成員或擴充套件函式,那麼會呼叫該函式來 建立屬性委託例項。

providedelegate 的乙個可能的使用場景是在建立屬性時(而不僅在其 getter 或 setter 中)檢查屬性一致性。

例如,如果要在繫結之前檢查屬性名稱,可以這樣寫:

class resourceloader(id: resourceid) 

private fun checkproperty(thisref: myui, name: string)

}fun bindresource(id: resourceid): resourceloader

class myui

providedelegate 的引數與 getvalue 相同:

在建立 myui 例項期間,為每個屬性呼叫 providedelegate 方法,並立即執行必要的驗證。

如果沒有這種攔截屬性與其委託之間的繫結的能力,為了實現相同的功能, 你必須顯式傳遞屬性名,這不是很方便

// 檢查屬性名稱而不使用「providedelegate」功能

class myui

fun myui.bindresource(

id: resourceid,

propertyname: string

): readonlyproperty

在生成的**中,會呼叫 providedelegate 方法來初始化輔助的 prop$delegate 屬性。 比較對於屬性宣告 val prop: type by mydelegate() 生成的**與 上面(當 providedelegate 方法不存在時)生成的**

class c 

// 這段**是當「providedelegate」功能可用時

// 由編譯器生成的**:

class c

請注意,providedelegate 方法只影響輔助屬性的建立,並不會影響為 getter 或 setter 生成的**

Kotlin學習 Kotlin列舉類

列舉類最基本的用法是實現乙個型別安全的列舉。列舉常量用逗號分隔,每個列舉常量都是乙個物件 enum class color每乙個列舉都是列舉類的例項,它們可以被初始化 enum class color val rgb int 預設名稱為列舉字元名,值從0開始。若需要指定值,則可以使用其建構函式 en...

Kotlin學習筆記(三) Kotlin密封類

前言 密封類,可以理解為列舉,規定了有限個型別,不可以存在其他型別,但列舉每個列舉常量只存在乙個示例,但是密封類的子類可以有多個示例,所以可以將密封類看做是列舉的拓展,基於列舉,高於列舉,青出於藍而勝於藍。正文密封類和普通類的區別,在於密封類使用了sealed修飾符,雖然密封類也可以有子類,但是所有...

kotlin學習途徑

google i o 2017 首日 keynote 剛剛落幕,其中 google 宣布 android 系統開發全面支援kotlin 算是乙個非常重磅的新聞。之前對kotlin不是很了解,所以這段時間專門去學習了解這麼語言。最好的學習途徑就是去kotlin官網去學習 1.kotlin官方中文翻譯文...