<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 委托屬性 [TOC] 有一些常見的屬性類型,雖然我們可以在每次需要的時候手動實現它們,但是如果能夠為大家把他們只實現一次并放入一個庫會更好。例如包括: * 延遲屬性(lazy properties): 其值只在首次訪問時計算; * 可觀察屬性(observable properties): 監聽器會收到有關此屬性變更的通知; * 把多個屬性儲存在一個映射(map)中,而不是每個存在單獨的字段中。 為了涵蓋這些(以及其他)情況,Kotlin 支持 _委托屬性_: ```kotlin class Example { var p: String by Delegate() } ``` 語法是: `val/var <屬性名>: <類型> by <表達式>`。在 *by*{:.keyword} 后面的表達式是該 _委托_,因為屬性對應的 `get()`(與 `set()`)會被委托給它的 `getValue()` 與 `setValue()` 方法。屬性的委托不必實現任何的接口,但是需要提供一個 `getValue()` 函數(與 `setValue()`——對于 *var*屬性)。 例如: ```kotlin import kotlin.reflect.KProperty class Delegate { 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.") } } ``` 當我們從委托到一個 `Delegate` 實例的 `p` 讀取時,將調用 `Delegate` 中的 `getValue()` 函數,所以它第一個參數是讀出 `p` 的對象、第二個參數保存了對 `p` 自身的描述(例如你可以取它的名字)。 例如: ```kotlin val e = Example() println(e.p) ``` 輸出結果: ``` Example@33a17727, thank you for delegating ‘p’ to me! ``` 類似地,當我們給 `p` 賦值時,將調用 `setValue()` 函數。前兩個參數相同,第三個參數保存將要被賦予的值: ```kotlin e.p = "NEW" ``` 輸出結果: ``` NEW has been assigned to ‘p’ in Example@33a17727. ``` 委托對象的要求規范可以在[下文](http://www.kotlincn.net/docs/reference/delegated-properties.html#%E5%B1%9E%E6%80%A7%E5%A7%94%E6%89%98%E8%A6%81%E6%B1%82)找到。 請注意,自 Kotlin 1.1 起你可以在函數或代碼塊中聲明一個委托屬性,因此它不一定是類的成員。你可以在下文找到[其示例](http://www.kotlincn.net/docs/reference/delegated-properties.html#%E5%B1%80%E9%83%A8%E5%A7%94%E6%89%98%E5%B1%9E%E6%80%A7%E8%87%AA-11-%E8%B5%B7)。 ## 標準委托 Kotlin 標準庫為幾種有用的委托提供了工廠方法。 ### 延遲屬性 Lazy [`lazy()`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/lazy.html) 是接受一個 lambda 并返回一個 `Lazy <T>` 實例的函數,返回的實例可以作為實現延遲屬性的委托:第一次調用 `get()` 會執行已傳遞給 `lazy()` 的 lambda 表達式并記錄結果,后續調用 `get()` 只是返回記錄的結果。 ```kotlin val lazyValue: String by lazy { println("computed!") "Hello" } fun main() { println(lazyValue) println(lazyValue) } ``` 默認情況下,對于 lazy 屬性的求值是**同步鎖的(synchronized)**:該值只在一個線程中計算,并且所有線程會看到相同的值。如果初始化委托的同步鎖不是必需的,這樣多個線程可以同時執行,那么將`LazyThreadSafetyMode.PUBLICATION` 作為參數傳遞給 `lazy()` 函數。而如果你確定初始化將總是發生在與屬性使用位于相同的線程,那么可以使用 `LazyThreadSafetyMode.NONE` 模式:它不會有任何線程安全的保證以及相關的開銷。 ### 可觀察屬性 Observable [`Delegates.observable()`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.properties/-delegates/observable.html) 接受兩個參數:初始值與修改時處理程序(handler)。 每當我們給屬性賦值時會調用該處理程序(在賦值*后*執行)。它有三個參數:被賦值的屬性、舊值與新值: ```kotlin import kotlin.properties.Delegates class User { var name: String by Delegates.observable("<no name>") { prop, old, new -> println("$old -> $new") } } fun main() { val user = User() user.name = "first" user.name = "second" } ``` 如果你想能夠截獲一個賦值并“否決”它,就使用 [`vetoable()`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.properties/-delegates/vetoable.html) 取代 `observable()`。 在屬性被賦新值生效*之前*會調用傳遞給 `vetoable` 的處理程序。 ## 把屬性儲存在映射中 一個常見的用例是在一個映射(map)里存儲屬性的值。這經常出現在像解析 JSON 或者做其他“動態”事情的應用中。在這種情況下,你可以使用映射實例自身作為委托來實現委托屬性。 ```kotlin class User(val map: Map<String, Any?>) { val name: String by map val age: Int by map } ``` 在這個例子中,構造函數接受一個映射參數: ```kotlin val user = User(mapOf( "name" to "John Doe", "age" to 25 )) ``` 委托屬性會從這個映射中取值(通過字符串鍵——屬性的名稱): ```kotlin class User(val map: Map<String, Any?>) { val name: String by map val age: Int by map } fun main() { val user = User(mapOf( "name" to "John Doe", "age" to 25 )) //sampleStart println(user.name) // Prints "John Doe" println(user.age) // Prints 25 //sampleEnd } ``` 這也適用于 *var*{:.keyword} 屬性,如果把只讀的 `Map` 換成 `MutableMap` 的話: ```kotlin class MutableUser(val map: MutableMap<String, Any?>) { var name: String by map var age: Int by map } ``` ## 局部委托屬性(自 1.1 起) 你可以將局部變量聲明為委托屬性。例如,你可以使一個局部變量惰性初始化: ```kotlin fun example(computeFoo: () -> Foo) { val memoizedFoo by lazy(computeFoo) if (someCondition && memoizedFoo.isValid()) { memoizedFoo.doSomething() } } ``` `memoizedFoo` 變量只會在第一次訪問時計算。如果 `someCondition` 失敗,那么該變量根本不會計算。 ## 屬性委托要求 這里我們總結了委托對象的要求。 對于一個**只讀**屬性(即 *val* 聲明的),委托必須提供一個名為 `getValue` 的函數,該函數接受以下參數: * `thisRef` —— 必須與 _屬性所有者_ 類型(對于擴展屬性——指被擴展的類型)相同或者是它的超類型; * `property` —— 必須是類型 `KProperty<*>` 或其超類型。 這個函數必須返回與屬性相同的類型(或其子類型)。 對于一個**可變**屬性(即 *var*{:.keyword} 聲明的),委托必須*額外*提供一個名為 `setValue` 的函數,該函數接受以下參數: * `thisRef` —— 同 `getValue()`; * `property` —— 同 `getValue()`; * new value —— 必須與屬性同類型或者是它的子類型。 `getValue()` 或/與 `setValue()` 函數可以通過委托類的成員函數提供或者由擴展函數提供。當你需要委托屬性到原本未提供的這些函數的對象時后者會更便利。兩函數都需要用 `operator` 關鍵字來進行標記。 委托類可以實現包含所需 `operator` 方法的 `ReadOnlyProperty` 或 `ReadWriteProperty` 接口之一。這倆接口是在 Kotlin 標準庫中聲明的: ```kotlin interface ReadOnlyProperty<in R, out T> { operator fun getValue(thisRef: R, property: KProperty<*>): T } interface ReadWriteProperty<in R, T> { operator fun getValue(thisRef: R, property: KProperty<*>): T operator fun setValue(thisRef: R, property: KProperty<*>, value: T) } ``` ### 翻譯規則 在每個委托屬性的實現的背后,Kotlin 編譯器都會生成輔助屬性并委托給它。 例如,對于屬性 `prop`,生成隱藏屬性 `prop$delegate`,而訪問器的代碼只是簡單地委托給這個附加屬性: ```kotlin class C { var prop: Type by MyDelegate() } // 這段是由編譯器生成的相應代碼: class C { private val prop$delegate = MyDelegate() var prop: Type get() = prop$delegate.getValue(this, this::prop) set(value: Type) = prop$delegate.setValue(this, this::prop, value) } ``` Kotlin 編譯器在參數中提供了關于 `prop` 的所有必要信息:第一個參數 `this` 引用到外部類 `C` 的實例而 `this::prop` 是 `KProperty` 類型的反射對象,該對象描述 `prop` 自身。 請注意,直接在代碼中引用[綁定的可調用引用](http://www.kotlincn.net/docs/reference/reflection.html#%E7%BB%91%E5%AE%9A%E7%9A%84%E5%87%BD%E6%95%B0%E4%B8%8E%E5%B1%9E%E6%80%A7%E5%BC%95%E7%94%A8%E8%87%AA-11-%E8%B5%B7)的語法 `this::prop` 自 Kotlin 1.1 起才可用。 ### 提供委托(自 1.1 起) 通過定義 `provideDelegate` 操作符,可以擴展創建屬性實現所委托對象的邏輯。 如果 `by` 右側所使用的對象將 `provideDelegate` 定義為成員或擴展函數,那么會調用該函數來創建屬性委托實例。 `provideDelegate` 的一個可能的使用場景是在創建屬性時(而不僅在其 getter 或 setter 中)檢測屬性一致性。 例如,如果要在綁定之前檢測屬性名稱,可以這樣寫: ```kotlin class ResourceDelegate<T> : ReadOnlyProperty<MyUI, T> { override fun getValue(thisRef: MyUI, property: KProperty<*>): T { ... } } class ResourceLoader<T>(id: ResourceID<T>) { operator fun provideDelegate( thisRef: MyUI, prop: KProperty<*> ): ReadOnlyProperty<MyUI, T> { checkProperty(thisRef, prop.name) // 創建委托 return ResourceDelegate() } private fun checkProperty(thisRef: MyUI, name: String) { …… } } class MyUI { fun <T> bindResource(id: ResourceID<T>): ResourceLoader<T> { …… } val image by bindResource(ResourceID.image_id) val text by bindResource(ResourceID.text_id) } ``` `provideDelegate` 的參數與 `getValue` 相同: * `thisRef` —— 必須與 _屬性所有者_ 類型(對于擴展屬性——指被擴展的類型)相同或者是它的超類型; * `property` —— 必須是類型 `KProperty<*>` 或其超類型。 在創建 `MyUI` 實例期間,為每個屬性調用 `provideDelegate` 方法,并立即執行必要的驗證。 如果沒有這種攔截屬性與其委托之間的綁定的能力,為了實現相同的功能,你必須顯式傳遞屬性名,這不是很方便: ```kotlin // 檢測屬性名稱而不使用“provideDelegate”功能 class MyUI { val image by bindResource(ResourceID.image_id, "image") val text by bindResource(ResourceID.text_id, "text") } fun <T> MyUI.bindResource( id: ResourceID<T>, propertyName: String ): ReadOnlyProperty<MyUI, T> { checkProperty(this, propertyName) // 創建委托 } ``` 在生成的代碼中,會調用 `provideDelegate` 方法來初始化輔助的 `prop$delegate` 屬性。 比較對于屬性聲明 `val prop: Type by MyDelegate()` 生成的代碼與[上面](http://www.kotlincn.net/docs/reference/delegated-properties.html#%E7%BF%BB%E8%AF%91%E8%A7%84%E5%88%99)(當 `provideDelegate` 方法不存在時)生成的代碼: ```kotlin class C { var prop: Type by MyDelegate() } // 這段代碼是當“provideDelegate”功能可用時 // 由編譯器生成的代碼: class C { // 調用“provideDelegate”來創建額外的“delegate”屬性 private val prop$delegate = MyDelegate().provideDelegate(this, this::prop) var prop: Type get() = prop$delegate.getValue(this, this::prop) set(value: Type) = prop$delegate.setValue(this, this::prop, value) } ``` 請注意,`provideDelegate` 方法只影響輔助屬性的創建,并不會影響為 getter 或 setter 生成的代碼。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看