Kotlin紹介シリーズ(三)高級用法のDelegation

4643 ワード

エージェントモードは、任意のテンプレートを必要とせずに、継承を実現するための優れた代替手段であることが証明されている。
プロキシモード
Delegationを知る前に、プロキシモードを復習して、その使用シーンを振り返る必要があります。不明瞭な読者はこの文章を読むことができる。ここでは、この文章を重点的に引用して、応用シーンについてのまとめを行います。
リモートエージェント:異なるアドレス空間において、オブジェクトに局所的な代表を提供し、このようにシステムはServer部分の事項を隠すことができる。仮想エージェント:1つのエージェントを使用してリソースを十分に消費するオブジェクトを表し、本当に必要な時に作成します。セキュリティエージェント:実際のオブジェクトのアクセスを制御するための権限です。スマートガイド:本物のオブジェクトを呼び出すと、エージェントは別のことを処理します。例えば、真のオブジェクトの参照カウントを計算します。オブジェクトが参照されていない場合、自動的にそれを解放します。または実際のオブジェクトにアクセスするときは、他のオブジェクトが変更できないようにロックできるかどうかを確認します。
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 main(args: Array) {
   val b = BaseImpl(10)
   Derived(b).print() // prints 10
}
ここでは短い文byを通じてbという動的エージェントを決定しました。bはDerived類のオブジェクトとして、コンパイラはそれのためにすべてのBaseのインターフェース方法を生成します。そして、本当にプロキシが必要な場合、プロキシされたクラスのインスタンスをパラメータとしてプロキシクラスを実装し、インターフェース方法を呼び出して動的エージェントを実現することができる。これは明らかにJavaが反射を利用して代理を実現するよりずっと便利です。
Delegated Propties
いくつかの属性があります。私たちは毎回必要な時に彼らを実現することができますが、一つの方法は一回だけ実現することができます。いくつかの場面があります。
  • Lazy properties:初めての訪問時にのみ計算される属性
  • observable properties:変化を待ち受ける属性
  • は、上記の需要を満たすために、分散されたfeildではなく、属性をmapに存在させ、Kotlinはプロキシ属性delegate propertiesを発表しました。
  • class Example {
        var p: String by Delegate()
    }
    
    メンバーpに対応するgetとset方法はいずれもDelegateのgetとsetメソッドによってプロキシされます。Delegateはどんなインターフェースを実現する必要がありませんが、get方法を提供しなければなりません。varタイプの場合、set方法も提供しなければなりません。eg:
    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.'")
        }
    }
    
    プロパティpはDelegateの一例となり、pを読み込むとDelegateのgetValueメソッドが呼び出され、最初のパラメータはプロキシp属性があるクラスのインスタンスを表し、第二のパラメータはp属性自体である。たとえば:
    val e = Example()
    println(e.p) // will print "Example@33a17727, thank you for delegating ‘p’ to me!"
    e.p = "NEW" // will print "NEW has been assigned to ‘p’ in Example@33a17727."
    
    注意すべきなのは、getValueとset Value方法の前にoperaterを追加しなければならないことです。これはDelegate類が実際にシステム標準ライブラリのインターフェースを実現しているため、一致していなければなりません。
    標準ライブラリ
    Kotlin標準ライブラリはいくつかの一般的な標準的なエージェントを提供しています。正直に言うと、lazy以外の柔軟代理店はまだ場面を発見していません。見つけたら必ず来て補充します。
    lazy
    lazyは一つの方法です。これは、プロキシ属性のためのLamda関数を導入することによって、Lazyのインスタンスを返すことができる。最初のget呼び出しでは、着信Lazy()のlamda関数が実行され、戻り値が記録され、その後の呼び出しは最初のレコードの値だけ戻ります。たとえば:
    val lazyValue: String by lazy {
        println("computed!")
        "Hello"
    }
    fun main(args: Array) {
        println(lazyValue)
        println(lazyValue)
    }
    
    印刷結果はこうです。
    computed!
    Hello
    Hello
    
    スレッドが安全であれば、ブロックを使ってLazy()を使います。同じように動作しますが、その値は一つのスレッドでしか計算されず、すべてのスレッドが同じ値を取得することを保証します。
    用途:私達の生命種類のメンバーの時、多くの時まだ初期化を必要としないで、この時、私達は初期化の構造関数でlazyのパラメーターとすることができて、それから代行の属性を形成します。たとえば:
    private val bannerAdapter: BannerAdapter by lazy { BannerAdapter() }
    val viewPager: ViewPager by lazy { ViewPager(context) }
    private val indicators: LinearLayout by lazy { LinearLayout(context) }
    private val tvTitle: JumpShowTextView by lazy { JumpShowTextView(context) }
    private val tvSlogan: JumpShowTextView by lazy { JumpShowTextView(context) }
    
    バブルブル
    Delegates.observableには二つのパラメータがあります。初期値と修正のためのhandler。この属性の値を送るたびに、ハンドルが呼び出されます。このHandlerには三つのパラメータがあります。割り当てられた属性、古い値、新しい値があります。
    import kotlin.properties.Delegates
    class User {
        var name: String by Delegates.observable("") {
            prop, old, new ->
            println("$old -> $new")
        }
    }
    fun main(args: Array) {
        val user = User()
        user.name = "first"
        user.name = "second"
    }
    
    この例は次のように印刷されます。
     -> first
    first -> second
    
    map
    Delegates.mapValは、mapのインスタンスを持ち、その中の属性をmapから読むことができるエージェントを返す。JSONを解析するとか、他のいくつかの「動的」なことをするなど、応用には多くの例がある。
    class User(val map: Map) {
        val name: String by Delegates.mapVal(map)
        val age: Int     by Delegates.mapVal(map)
    }
    
    この例では、構造関数はmapを持っています。
    val user = User(mapOf (
        "name" to "John Doe",
        "age" to 25
    ))
    
    エージェントはこのmapから指を取る(属性の名前を通して):
    println(user.name) // Prints "John Doe"
    println(user.age)  // Prints 25
    
    varはmapVarが使えます