デリゲートプロパティ
Delegated propaties
Delegated propertyにすることで、プロパティに対して、遅延初期化や値の監視などをかんたんに実装することができる。
デリゲートの指定
var delegatedPropety: Type by Delegate()
型の後ろに、by
キーワードを使ってデリゲートを指定する。
Standard Delegate
Delegateは自作することができるが、Kotlinは標準で以下のDelegateを用意している。
- Lazy properties
- Observable properties
- 他のプロパティへのデリゲート
- Storing properties in map
Lazy properties
// lazy()関数は、初期化するための関数リテラルを受け取り、Lazy<T>インスタンスを返す val lazyValue: String by lazy { println("computed") "Hello" }
lazy()
デリゲートをつけることで、Lazy<T>
インスタンスが返され、
初回のget()
が実行された際に、lazy()に渡したラムダが実行される。
ローカル変数をデリゲートプロパティとして宣言することができる。
fun example(computeFoo: () -> Foo) { val memoizeFoo by lazy(computeFoo)
Observable properties
Delegates.observable()
は2つの引数を取る: 初期値と変更のハンドラー。
import kotlin.properties.Delegate class User { var name: String by Delegates.ovservable("<no name>") { prop, old, new -> println("$old -> $new") } }
他のプロパティへのデリゲート
プロパティは、他のプロパティに対してそのgetterとsetterをデリゲートすることができる。
デリゲートプロパティになれるのは、
- top-level property
- 同じクラスのメンバ、拡張プロパティ
- 他のクラスのメンバ、拡張プロパティ
var topLevelInt: Int = 0 class MyClass { var delegatedToTopLevel: Int by ::topLevelInt }
class ClassWithDelegate(val anotherClassInt: Int) class MyClass(var memberInt: Int, val anotherClassInstance: ClassWithDelegate(val anotherClassInt: Int) { var ddelegedToMember: Int by this::memberInt val delegatedToAnotherClass: Int by anotherClassInstance::anotherClassInt }
var MyClass.extDelegated: Int by ::topLevelInt
この機能は、deprecatedに対して有効
clas MyClass { var newName: Int = 0 @Deprecated("Use newName insted", ReplaceWith("newName")) var oldName: Int by this::newName }
Storing properties in a map
class User(val map: Map<String, Any?>) { val name: String by map // mapプロパティに移譲 val age: Int by map }
name
やage
はmapから取得される
val json = mapOf("name" to "John Doe", "age" to 25) val user = User(json) println(user.name) // John Doe println(user.age) // 25
自作Delegate
valプロパティに対しては、getValue()
を実装する必要がある。
varプロパティに対しては、setValue()
も実装する必要がある。
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.") } }
以下のように使える
class Example { var p: String by Delegate() }