Kotlinでは、委譲プロパティ (delegated properties) に対する組み込みサポートがあります。byメカニズムを使用すると、特定のオブジェクト(委任者)にプロパティのゲッター/セッターを委譲できます。よく知られている委任者には、lazy、observable、vetoable、およびカスタム委任者があります。
利点:
カスタム委任者の例:
class UpperCaseDelegate { private var value: String = "" operator fun getValue(thisRef: Any?, property: KProperty<*>): String = value operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: String) { value = newValue.uppercase() } } class Person { var name: String by UpperCaseDelegate() }
制限:
コンテキスト(たとえば、Android Context)へのアクセスを必要とする委任者を、コンパニオンオブジェクトやトップレベルオブジェクトのプロパティに使用できますか?
「できるに決まっている、なぜできないのか?」と間違った回答をすることがよくあります。
正しい回答: いいえ、なぜならコンパニオンオブジェクトやトップレベルオブジェクトはクラスまたはアプリケーションのインスタンスの初期化前に初期化されるため、未初期化のコンテキストにアクセスすることによるエラーが発生する可能性があるからです。インスタンスへのアクセスを必要とする委任者は、クラスのプロパティでのみ使用する必要があります。
事例
Android-ViewModelにおけるレイジー初期化の委譲: プログラマーはheavy-lazy委任者をコンパニオンオブジェクトに移動しました。いくつかの状況(SDK更新後)では、アプリケーションが初期化中にクラッシュしました—コンテキストがまだ利用できず、委任者はすでに「init」を実行していました。
事例
委任者による不適切なシリアル化: データを保持するためにカスタム委任者を使用しましたが、シリアル化できないコンテキストへの参照を含んでいました。シリアル化の試行中にエラーが発生し、データが失われました。
事例
コールバックエラーのあるObservable委任者: 開発者はDelegates.observableを使用して状態を制御しましたが、ラムダ内で新しい値を代入することがループを引き起こし、ランタイムでStackOverflowErrorが発生しました。