Kotlin Reference (十四) 委託類和委託屬性
KotLin 相關文件
Kotlin委託機制
委託模式是軟體設計模式中的一項基本技巧。在委託模式中,有兩個物件參與處理同一個請求,接受請求的物件將請求委託給另一個物件來處理。
Kotlin 直接支援委託模式,更加優雅,簡潔。Kotlin 通過關鍵字 by 實現委託。
委託類 (Class Delegation)
例,
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() {
print(x)
}
}
class Derived(b: Base) : Base by b {
fun add() {
}
}
fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print() // prints 10
Derived(b).add()
}
在基類列表上使用by
子句,表示,Derived類的內部物件b,會生成Base的所有公共函式。這時呼叫Derived的屬於Base的公共函式,就會委託呼叫b物件的對應函式。
Derived是一個委託類,其對應的委託例項就是建構函式中Base的例項b
委託屬性 (Delegated Properties)
有一些常見的屬性,雖然我們可以每次需要時手動實現它們,但現在只需要如下形式的使用,即可實現。這些屬性,如:
lazy properties
只在第一次訪問時進行值計算observable properties
監聽獲取關於屬性變化的通知storing properties
將屬性儲存在一個Map中
委託屬性語法
例,
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.'")
}
}
class Example {
var p: String by Delegate()
}
fun main(args: Array<String>) {
val example = Example()
example.p = "stone"
println(example.p)
}
輸出:
stone has been assigned to 'p in [email protected]238e0d81.'
[email protected]238e0d81, thank you for delegating 'p' to me!
委託屬性語法:
val/var <property name>: <Type> by <expression>
如,上例中的Example的屬性p
這裡將屬性p的get/set委託給Delegate類的getValue/setValue函式來處理。
屬性委託類,如Delegate類,其可以在形式上繼承/實現一個基類/介面(這裡指非特定的),然而該介面的函式在外部並不能由委託屬性呼叫,所以繼承/實現的特性,在這裡並沒有什麼大的意義
標準委託屬性
Kotlin標準庫,為如下幾種有用的委託形式,提供了相應的工廠方法
- lazy
val lazyValue: String by lazy {
println("computed!") //多次呼叫 只會輸出一次
"Hello"
}
檢視lazy原始碼,發現它呼叫的是:
public fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
SynchronizedLazyImpl這是一個同步執行的函式,內部實現是加了synchronized的同步程式碼塊的;所以在多執行緒環境中,第一個進入同步程式碼塊執行出的結果,就是lazy-property的值。
再看lazy的一個過載方法:
public fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
when (mode) {
LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
}
使用,如
val lazyValue: String by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {...}
LazyThreadSafetyMode對應的三種模式:
a. SYNCHRONIZED 同步
b. PUBLICATION 釋出。內部的計算過程可能被多次訪問,但內部的屬性是一個Volatile修飾的屬性,所以在多執行緒環境中,被第一次訪問獲取資料後,此後的其它執行緒都共享該值。
c. NONE
未對多執行緒環境,做任何處理。
所以在多執行緒環境中使用,會造成:計算和返回值都可能處在多個執行緒中。
注:lazy屬性,只能宣告為 val的,即它是隻能get的
- Observable
需要在屬性後跟by Delegates.observable()
,即呼叫這個方法設定屬性值。該方法是兩個引數:初始值,lambda表示式(即處理值的語句塊)。
例,
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("<no name>") {
property, oldValue, newValue ->
println("$oldValue -> $newValue") }
}
fun main(args: Array<String>) {
val user = User()
user.name = "first"
user.name = "second"
}
輸出:
<no name> -> first
first -> second
Delegates.observable()中,使用的lambda表示式,其引數:
I. property:在這指屬性name
II. oldValue: 舊值,首次即為指定的初始值
III. newValue:新set的值
最終屬性值=new值
還有個Delegates.vetoable(),跟Delegates.observable()類似,只是lambda表示式的返回值為Boolean。
返回true:屬性值=new值
返回false:屬性值=old值observable 屬性,可以定為val的,不過既然要監聽屬性的變化,還是用var較好
- Storing Properties in a Map
將屬性委託給(存進)一個Map。
例,
class Per(val map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}
fun main(args: Array<String>) {
val per = Per(mapOf("name" to "John Doe", "age" to 25))
println(per.name)
println(per.age)
}
輸出:
John Doe
25
也可以使用一個
var map
,即map的型別為MutableMap
區域性委託屬性
在kotlin1.1後,可以定義區域性的委託屬性。如,
class Foo {
fun isValid(): Boolean {
return Random().nextBoolean()
}
fun doSomething() {
println("doSomething")
}
}
fun example(computeFoo: () -> Foo) {
val memoizedFoo by lazy(computeFoo) //memoizedFoo: Foo
if (memoizedFoo.isValid()) {
memoizedFoo.doSomething()
}
// var p: String by Delegate() //可以有其它形式的委託屬性
}
fun main(args: Array<String>) {
example {
println("生成Foo")
Foo()
}
}
上例的example(),接受一個函式引數,該函式返回Foo型別物件。
memoizedFoo,就是一個區域性委託屬性;這裡委託的是public fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
這個函式,該lazy函式接受一個函式引數。
區域性委託屬性也可以是其它的委託屬性形式。
委託屬性給一個類例項之總結
對於一個val修飾的只讀屬性
它的委託必須提供一個名為
getValue
的函式。該函式中的兩個引數的意義,如下
thisRef : 其型別,必須是委託屬性的所有者或其super型別
property : 必須是KProperty<*>
或其super型別KCallable<*>
對於一個var修飾的可變屬性
其含有引數,除了上述兩個外,還多一個new-value值
注意getValue/setValue函式的宣告以operator開始
這兩個函式,可以直接定義成委託類的成員函式,或委託類的擴充套件函式。後者對於在原始類中未提供它們時,提供了方便
ReadOnlyProperty 和 ReadWriteProperty
屬性的委託類,可以實現兩個介面,kotlin.properties.ReadOnlyProperty
和kotlin.properties.ReadWriteProperty
;這兩個介面中,聲明瞭getValue(var和val屬性)/setValue(var屬性)函式。例,
class Example1: Super() {
val p1: String by Delegate1()
}
class Delegate1: ReadOnlyProperty<Example1, String> {
override fun getValue(thisRef: Example1, property: KProperty<*>): String {
return ""
}
}
不實現這兩個介面也是可以的,如之前使用 operator宣告的getValue/setValue函式
委託屬性轉化規則
宣告的委託屬性,會由編譯器產生一個隱藏屬性,且會生成對應的get/set方法。
如,
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)
}
provideDelegate函式提供委託例項
在kotlin1.1,提供了provideDelegate函式,它可以為屬性提供物件委託邏輯。
如果 by 右側所使用的物件將 provideDelegate(函式名不能隨意變更) 定義為成員或擴充套件函式,那麼會呼叫該函式來 建立屬性委託例項;且要求委託例項的類,要實現 ReadOnlyProperty 或 ReadWriteProperty介面
例,
class Dele(val id: String): ReadOnlyProperty<MyUI, String> {
override fun getValue(thisRef: MyUI, property: KProperty<*>): String {
return "${UUID.randomUUID()}_$id"
}
}
class ResourceID {
private constructor(id: String) {
this.id = id
}
open val id: String
companion object {
val image_id = ResourceID("1")
val text_id: ResourceID = ResourceID("2")
}
}
class ResourceLoader(resId: ResourceID) {
private val resourceID = resId
operator fun provideDelegate(thisRef: MyUI, prop: KProperty<*>): ReadOnlyProperty<MyUI, String> {
checkProperty(thisRef, prop.name)
// create delegate
return Dele(resourceID.id+"")
}
private fun checkProperty(thisRef: MyUI, name: String) {
if (name.equals("image")) {
//...
}
}
}
fun MyUI.bindResource(id: ResourceID): ResourceLoader {
return ResourceLoader(id)
}
class MyUI {
val image by bindResource(ResourceID.image_id) //bindResource()產生委託物件
val text by bindResource(ResourceID.text_id)
}
fun main(args: Array<String>) {
println("image-id = " + MyUI().image)
println("txt-id = " + MyUI().text)
}
上例中,找到by關鍵字,其右側呼叫了bindResource建立委託類例項。
bindResource(這是一個擴充套件函式)返回ResourceLoader型別的例項。
ResourceLoader中定義了一個operator宣告的函式,名為provideDelegate,其返回真正的委託類Dele的例項物件。
Dele中,getValue函式,返回一個”拼接了UUID字首+資源型別id”的字串。
使用provideDelegate函式的委託屬性轉化規則
class C {
var prop: Type by MyDelegate()
}
// this code is generated by the compiler
// when the 'provideDelegate' function is available:
class C {
// calling "provideDelegate" to create the additional "delegate" property
private val prop$delegate = MyDelegate().provideDelegate(this, this::prop)
val prop: Type
}
相關推薦
Kotlin Reference (十四) 委託類和委託屬性
KotLin 相關文件 Kotlin委託機制 委託模式是軟體設計模式中的一項基本技巧。在委託模式中,有兩個物件參與處理同一個請求,接受請求的物件將請求委託給另一個物件來處理。 Kotlin 直接支援委託模式,更加優雅,簡潔。Kotlin
Kotlin筆記(四)——類和類的構造器
從這篇文章開始,我們進入Kotlin面向物件程式設計的世界,這部分的內容相對較多,也是十分重要的,我儘可能將我所學到的介紹給大家。 可見性修飾符 在開始介紹類之前,先來簡單說明一下Kotlin的可見
python自動化運維學習第十四天--類的屬性和方法總結
類的屬性 類屬性(公有屬性) 類的私有屬性 物件的公有屬性 物件的私有屬性 內建屬性 函式的區域性變數 全域性變數 #!/usr/bin/python # -*- coding:utf-8 -*- class MyClass(object): var1 = '類屬性,類的公有
從零開始學習html(十四)單位和值
type 當我 總結 學生 專註 bfc span blog 設置顏色 一、顏色值 1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5
《C#圖解教程》讀書筆記之四:類和繼承
intern html pan 類中訪問 ted obj 小寫 his new 本篇已收錄至《C#圖解教程》讀書筆記目錄貼,點擊訪問該目錄可獲取更多內容。 一、萬物之宗:Object (1)除了特殊的Object類,其他所有類都是派生類,即使他們沒有顯示基類定義。
Python學習筆記(二十四)StringIO和BytesIO
nbsp from 寫入 enc print == world! byte 初始化 StringIO 很多時候,數據讀寫不一定是文件,也可以在內存中讀寫。 StringIO顧名思義就是在內存中讀寫str。 要把str寫入StringIO,我們需要先創建一個String
AGG第三十四課 stroke_aa和outline_aa渲染線段效率對比
agg outline_aa stroke1 渲染代碼 void TestStrokeAAPerformance() { agg::rendering_buffer &rbuf = rbuf_window(); agg::pixfmt_bgr24 pixf(rbuf); typedef
C#圖解教程 第二十四章 反射和特性
疑問 兩個 全局 代碼行數 owin info 技術 ole jpg 反射和特性元數據和反射Type 類獲取Type對象什麽是特性應用特性預定義的保留的特性Obsolete(廢棄)特性Conditional特性調用者信息特性DebuggerStepThrough 特性其他預
第十二章 類和動態內存分配
其它 成對 關鍵字 類型 fault ptr 釋放緩存 別名 返回 靜態成員變量 不能在類聲明中初始化靜態成員變量(聲明描述了如何分配內存,但並不分配內存); 可以在類聲明之外(在方法文件中)用單獨的語句初始化靜態成員變量; 初始化語句必須指出類型,並使用作用域解析運算符,
十、密封類和密封方法
後繼 code pre lin 除了 virtual 抽象方法 限制 led 密封類 密封類可以用來限制擴展性,如果密封了某個類,則其他類不能從該類繼承。 如果密封了某個成員,則派生類不能重寫該成員的實現。 一個密封類是沒有也不能繼承的類,因此sealed關鍵字和abstr
SpringMVC(十四)類型轉換器
har public property springmvc gpo source 表達 resolv obj 為什麽頁面上輸入‘12’,可以復制給Handler方法對應的參數? 和是因為類型轉換器並不是可以將用戶提交的String,轉換為用戶需要的所有類型,此時,就需要自定
第二十七篇 類和對象相關知識
執行函數 自動 示例 需要 實例 __main__ 傳參 房子 類的定義 類和對象 1. 什麽叫類:類是一種數據結構,就好比一個模型,該模型用來表述一類食物(食物即數據和動作的結合體),用它來生產真是的物體(實例) 2. 什麽叫對象:睜開眼,你看到的一切事物都是一個個的對象
[隨筆][Java][讀書筆記][thinking in java][第十四章 類型信息]
found 構造 att main 數組 test 第一個 eating urn 主要理解如何在運行時獲取類型信息。主要有兩種方式:一是RTTI,假定我們在編譯時已經知道了所有的類型;二是反射機制,允許在運行時發現和使用類的信息。 14.1 為什麽需要RTTI 一個多
【練習題】第十六章--類和函式(Think Python)
class Time: hour=0 minute=0 second=0 def print_time(t): print("%.2d:%.2d:%.2d"%(t.hour,t.minute,t.second)) def is_after(t1,t2):
【練習題】第十五章--類和物件(Think Python)
別名有可能讓程式讀起來有困難,因為在一個位置做出的修改有可能導致另外一個位置發生不可預知的情況。這樣也很難去追蹤指向一個物件的所有變數。所以就可以不用別名,而用複製物件的方法。copy 模組包含了一個名叫 copy 的函式,可以複製任意物件: >>> p1 = Point()
斯坦福大學-自然語言處理入門 筆記 第十四課 CGSs和PCFGs
一、概率上下文無關文法((Probabilistic) Context-Free Grammars) 1、上下文無關文法(Context-Free Grammars) 我們也可以稱之為片語結構語法(Phrase structure grammars) 由四個成分構成G=
elasticsearch 筆記十四:_filter 和query 相關原理
GET /company/employee/_search { "query":{ "must":[ "match":{ "join_date": "2016-01-01" }
CLR via C#學習筆記-第十四章-字元和字串
14.1 字元 Char結構 Char結構提供的欄位 每個字元都是System.Char結構的例項,Char型別提供了兩個公共只讀常量欄位:MinValue('\0')和MaxValue('\uffff\)。 Char例項能呼叫的方法 為Char的例項呼叫靜態GetUnicodeCategory方法
菜雞的Java課筆記 第十四 String 類常用方法
/*String 類常用方法 將所有String類的常用方法全部記下來,包括方法名稱,引數作用以及型別 一個成熟的程式語言,除了它的語法非常完善之外,那麼也需要提供有大量的開發類庫 而需要知道的java
Unity 六邊形地圖系列(二十四) :地區和侵蝕
原文地址:https://catlikecoding.com/unity/tutorials/hex-map/part-24/ 機翻+個人潤色 在地圖周圍加一圈水域。 將地圖分割成多個區域。 對懸崖進行侵蝕。 移動土地來平滑地形。 這是關於六邊形地圖的系列教程