デリゲートパターンは、ソフトウェアのデザインパターンの基本的なスキルです。モードを委託では、別のオブジェクト処理に要求を委任するための要求を受信し、同じ要求を処理し、被験者に関与する2つのオブジェクトがあります。
Kotlin直接支援委任モード、よりエレガントな、シンプル。が委託キーワードによって達成Kotlin。
クラスデリゲート
つまり、呼び出し元のオブジェクトに定義されているデリゲートクラスのクラスメソッドは、実際に達成するための方法の別のクラスです。
派生クラスは、派生以下の実施例は、すべてのメソッド・インターフェースベース、及びこれらの方法を実施するために渡されたデリゲートオブジェクトの基本クラスを継承します。
//インタフェースの作成 インタフェース基本{ ファンプリント() } //このクラスインターフェース達成するために委託 基本{:クラスBaseImpl(:のIntヴァルX) オーバーライドファンプリント(){プリント(X)} } //キーワードをデリゲートクラス確立することにより によりベースB派生クラス(:ベースB) ファンメイン(引数:アレイの<string>){ ヴァルBaseImpl B =(10) 由来(B)の.print()//出力10 }
ステートメントに由来する、句によって、それが由来するの内部オブジェクト・インスタンスに格納されたBなり、コンパイラが生成する前記ベースインタフェースからすべてのメソッドを継承し、Bに呼を転送します。
プロパティの信頼
プロパティの手数料は、プロパティの値を参照するクラス定義で直接クラスではありませんが、それは、プロパティクラスの一元管理を実現するために、プロキシクラスに委託されます。
プロパティのデリゲートの構文:
ヴァル/ VAR <属性名>:<タイプ> による<式>
- VAR /ヴァル:属性型(変数/読み取り専用)
- プロパティ名:属性名
- タイプ:属性のデータ型
- 式:主要-エージェントクラス
式は、キーワードによって委託された後、プロパティ()メソッド(およびセット()メソッド)を取得、このオブジェクトgetValue()およびsetValue()メソッドに委託されます。プロパティの信頼が任意のインターフェイスを実装する必要はありませんが、のgetValue()関数(var属性のために、あなたものsetValue()関数が必要です)を提供しなければなりません。
委託クラスを定義します。
getValue()メソッドとのsetValue()メソッドを含むように、とオブジェクトクラスのパラメータは、デリゲートオブジェクト属性を支えるために、委託されthisRefそのような必要性。
kotlin.reflect.KPropertyインポート //定義では、属性、デリゲートクラスが含ま {クラスの例を :委任することによって文字列()のvar P } //デリゲートクラス クラスのデリゲート{ オペレータ楽しいのgetValue(thisRef?:、任意のプロパティ:KProperty <*>) :ストリング{ "$ {property.nameは}プロパティを委託$ thisRef、"リターン } 演算子ファンのsetValue(thisRef:?、任意プロパティ:KProperty <*>、値:文字列){ のprintln(「$ $ {プロパティthisRef .nameの}プロパティ値$に割り当てられた") } } ファンメイン(引数:アレイの<string>){ ヴァルE =例() プロパティにアクセス//のprintln(EP)は、()関数のgetValueを呼び出す EP =" Runoob「/ /のsetValue()関数を呼び出す のprintlnを(EP) }
標準委員会
Kotlin標準ライブラリには、特性を達成するために、工場のデリゲートメソッドの多くを築いてきました。
レイジー怠惰な性質
(レイジーに渡さ行う第1の呼のget():遅延()は、パラメータとしてラムダ式をとる関数は、関数がレイジー<T>例えば、遅延として実装インスタンス返さデリゲート属性が返され)ラムダ式のと)フォローアップコールを(だけ返されたレコードの結果を得るために、結果を記録。
LazyValueヴァル:{怠惰によって文字列 のprintln( "計算された!") "こんにちは" } 楽しいメイン(引数:配列<文字列>) { printlnを(LazyValueは)//最初のパフォーマンスが行われた二回出力表現 のprintln( "-------------") のprintln(LazyValueれる)//最初の性能は、二回出力発現を行います }
観察可能な観察可能なプロパティ
観測可能なオブザーバーパターンを実装するために使用することができます。
Delegates.observable()関数は2つの引数を受け入れる:最初は初期値、イベント(ハンドラ)に応答して第2の値の変化特性です。
割り当てられたプロパティ、古い値と新しい値:プロパティの割り当て後に3つのパラメータを持っているイベント(ハンドラ)、に応答して実行されます。
kotlin.properties.Delegatesインポート クラスユーザー{ VAR名:Delegates.observableによって文字列( "初期値") { 小道具、古い、新規作成- > のprintln( "旧:古$ - >新しい新しい:$新新") } } 楽しいメイン(引数:アレイの<string>) { ヴァル=ユーザーユーザー() user.name = "最初の割当" //古い:初期値-新しい>:最初の割り当て user.name = "第割り当て" / /旧:最初の代入- >新しい:二割り当てます }
結果:
旧:初期値- >新:最初の割り当て
歳:最初の代入- >新しい:第二の割り当て
マップに保存されている属性
一つの一般的なユースケースは、地図(マップ)の属性値が格納されています。これは、多くの場合、JSONを解析するかの問題で他の「ダイナミック」を行うなどのアプリケーションで発生します。このケースでは、デリゲートのプロパティを達成するために、デリゲートのインスタンス自体としてマップを使用することができます。
クラスのサイト(ヴァルマップ:地図<文字列、どれ?>) { valの名前:マップによって文字列 ヴァル・URL:マップによって文字列 } 楽しいメイン(引数:配列<文字列>) { valのサイト=サイト(mapOf( "名前"に"zhaosi"、 "URL"に"http://www.zhaosi.com" )) のprintln(site.name) のprintln(site.url) }
あなたはvar属性を使用する場合は、地図MutableMapを交換する必要があります。
クラスのサイト(ヴァルマップ:MutableMap <文字列、どれ?>){ valの名前:マップによって文字列 ヴァル・URL:マップによって文字列 } 楽しいメイン(引数:配列<文字列>) { VARマップ:MutableMap <?文字列、どれ> = mutableMapOf( "名前"に"zhaosi"、 "URL"と"www.zhaosi.com" ) ヴァル・SIT =サイト(マップ) のprintln(sit.name) のprintln(sit.url) のprintln( "------ ------ ") map.put("名前」、 "liuneng") map.put( "URL"、 "www.liuneng.com") のprintln(sit.name) のprintln(sit.url) }
nullではありません
notNullを初期化フェーズのプロパティの値を決定することができないような場合にも適用されます。
インポートkotlin.properties.Delegates クラスはFoo { VARのnotNullBar:Delegates.notNullによって文字列の<string>() } 楽しいメイン(引数:アレイの<string>) { VARのFOO =はFoo() foo.notNullBar = "バー" のprintln(FOO .notNullBar) }
プロパティが割り当て前にアクセスされた場合は、例外がスローされることに注意してください。
ローカルデリゲートプロパティ
あなたは、ローカル変数のデリゲートプロパティを宣言することができます。たとえば、ローカル変数の遅延初期化を行うことができます。
楽しい例(computeFoo:() - >はFoo){ 怠惰(computeFoo)によってヴァルmemoizedFoo IF(someCondition && memoizedFoo.isValid()){ memoizedFoo.doSomething() } }
memoizedFoo変数は、最初の訪問時にカウントされます。someConditionが失敗した場合、その変数は計算されません。
受託財産の要件
読み取り専用属性(つまり、valの属性である)の場合、そのデリゲートは、のgetValue()のと呼ばれる機能を提供しなければなりません。関数は以下のパラメーターを受け入れます
- thisRef - 所有者は、(拡張属性のために - 拡張のタイプを指す)タイプ属性する必要があり、同じ又はそのスーパータイプであります
- プロパティは - タイプKProperty <*>やスーパータイプでなければなりません。
この関数は、属性は、同じタイプ(またはサブタイプ)でなければなりませんを返します。
変数(可変)プロパティ(すなわち、プロパティVAR)の値を、のgetValue()関数を除いて、それはまた、)追加のデリゲートのsetValue(呼び出された関数である必要があり、この関数は以下のパラメーターを受け入れます。
- プロパティは - タイプKProperty <*>やスーパータイプでなければなりません。
- 新しい値は - プロパティまたはそのスーパータイプの同じタイプでなければなりません。
翻訳ルール
属性を達成するため、各デリゲートの後ろに、Kotlinコンパイラは、補助プロパティを生成し、それに委託します。例えば、属性propのために、隠し属性は$デリゲートを支える生成、およびアクセスコードは、単に追加のプロパティに委託されています。
C {クラス VARプロップ:タイプによるMyDelegate() } //これは、コンパイラ対応するコードによって生成される: クラスC { プライベートプロプヴァル$ = MyDelegateデリゲート() VAR支柱:タイプ GET()= $プロプdelegate.getValue (この、この::小道具) SET(値:タイプ)= $ delegate.setValue小道具(これ、これ::小道具、値) }
クラスCの外側にこの参照引数の最初のインスタンスを、これがKProperty ::プロップ反射型オブジェクト自体を支える説明である:Kotlinコンパイラパラメータが支柱に関するすべての必要な情報を提供しました。
コミッションを提供
定義provideDelegateオペレータによる拡張属性ロジック実装デリゲートオブジェクトを作成することができます。部材や拡張機能として定義provideDelegateに使用されるオブジェクトの右側による場合は、プロパティインスタンスを委任作成する関数を呼び出します。
あなたがプロパティを作成する際の一つの可能な利用シナリオprovideDelegateは、プロパティの整合性をチェックする(とのみならずそのゲッターやセッターで)です。
あなたが結合する前に、属性名をチェックしたい場合たとえば、あなたが書くことができます。
クラスResourceLoader <T>(ID:のResourceID <T>){ オペレータ楽しみprovideDelegate( thisRef:MyUI、 小道具:KProperty <*> ):ReadOnlyProperty <MyUI、T> { checkProperty(thisRef、prop.name) //创建委托 } プライベート楽しいcheckProperty(thisRef:MyUI、名:文字列){...} } 楽しい<T> bindResource(ID:のResourceID <T>):ResourceLoader <T> {...} クラスMyUI { bindResourceによってヴァル画像(ResourceID.image_id ) bindResource(ResourceID.text_idによってヴァルテキスト) }
ProvideDelegate同じパラメータとのgetValue:
- thisRef - 所有者は、(拡張属性のために - 拡張のタイプを指す)タイプ属性する必要があり、同じ又はそのスーパータイプであります
- プロパティ - タイプKProperty <*>やスーパータイプでなければなりません。
MyUIインスタンスの作成時に、各プロパティのprovideDelegateメソッドを呼び出して、すぐに必要な検証を行います。
委託プロパティと、そのような傍受の不在の間の結合の能力は、同じ機能を実現するためには、明示的に非常に便利ではありませんこれは、プロパティ名を渡す必要があります。
// "provideDelegate"機能の使用せずに、属性名を確認し 、クラスMyUI { bindResource(ResourceID.image_id、 "画像")によるヴァルイメージ bindResource(ResourceID.text_id、 "テキスト")によるヴァルテキスト } 娯楽<T> MyUI.bindResourceを( ID:のResourceID <T>、 プロパティ名:文字列 ):ReadOnlyPropertyは<MyUI、T> { 、checkProperty(この、プロパティ名)は、 デリゲートを作成する// }
生成されたコードでは、補助プロップ$デリゲートプロパティを初期化するためにprovideDelegateメソッドを呼び出します。比較特性宣言のヴァル小道具:上記で生成MyDelegateによるタイプ()コード(場合provideDelegate方法が存在していない)生成されたコード:
{クラスC :タイプによるMyDelegate()VARプロップ } 「provideDelegate」機能が利用可能になった場合//このコードは、 コンパイラによって生成//コード: クラスC { //追加の「委任」を作成する「provideDelegate」と呼んプロパティ プライベート小道具ヴァル$ = MyDelegateデリゲート()provideDelegate(これ、これは小道具::)。 ヴァルの小道具:タイプ GET()= $ delegate.getValue小道具(これ、これは小道具::) }
provideDelegateは二メソッドプロパティのみに影響し、ゲッターやセッターのためのコードの生成には影響しませんを作成して、注意してください。