こんにちは、関西Kotlin勉強会定期開催を目論んでいるtakuji31です。
これはKotlin Advent Calendar 2015、第2日目の記事です。
今日はKotlinのDelegationについて紹介します。
Delegationについて
委譲 (delegation) とはオブジェクト指向プログラミングにおいて、あるオブジェクトの操作を一部他のオブジェクトに代替させる手法のこと。
例えばクラスAのオブジェクトある処理を、クラスBに任せるといった仕組みですね。
- 継承をせずに一部の処理を拡張したい
- 同じ処理を別クラスに共通して実装したい
といった時に使うことが多いようです。
KotlinのDelegation
KotlinのDelegationは以下の2種類があります。
- Class Delegation
- Property Delegation
Class Delegation
Kotlinにおいて、オブジェクトの処理を別クラスのオブジェクトに委譲する仕組みです。
Class Delegation構文を使うと、あるクラスに別クラスの全てのメソッドを呼び出すためのメソッドを自動生成します。
interface A {
fun hoge()
val prop : String
}
class AImpl() : A {
override val prop : String
get() = "Delegation is pretty!"
override fun hoge() {
println("Hello delegation!")
}
}
class B(a : A) : A by a
class C(val a : A) : A by a {
override fun hoge() {
a.hoge()
println("Override method!")
}
}
fun main(args : Array<String>) {
val a = AImpl()
val b = B(a)
b.hoge() // Hello delegation!
println(b.prop) // Delegation is pretty!
val c = C(a)
c.hoge() // Hello delegation!(line break)Override method!
}
A by aと表記してやることで、インターフェースAで定義されているメソッドやプロパティーの処理を全てaオブジェクトに委譲します。
もちろんoverrideしてやることで、処理を拡張することもできます。
Property Delegation
Class Delegationがクラス全体の処理を委譲する仕組みに対して、Property Delegationはオブジェクトのプロパティー処理のみを委譲する仕組みです。
class DelegatedClass() {
var nonNullString : String by Delegates.notNull()
}
こちらもbyキーワードで特定のオブジェクト(上記のサンプルコードの場合はDelegates.notNull()で生成されたもの)に処理を移譲します。
こうしてやることで、nonNullStringは初期化不要ですが、getする前に必ず値をsetしてやらないといけないプロパティーになります。
ちなみにDelegatesはデフォルトでProperty Delegationを生成するメソッドがいくつか用意されているobjectです。
Property Delegationを実装する
もちろん自分でオブジェクトを定義することもできます。
ReadWriteProperty、もしくはReadOnlyPropertyを実装したクラスを用意しましょう。
今回はMapからStringをset/getするMapVarPropertyを作ってみます。
class MapVarProperty(val map : MutableMap<String, String>, val key : String? = null) : ReadWriteProperty<Any, String> {
override fun getValue(thisRef: Any, property: KProperty<*>): String {
val k = key ?: property.name
return map[k]!!
}
override fun setValue(thisRef: Any, property: KProperty<*>, value: String) {
val k = key ?: property.name
map[k] = value
}
}
class MapModel(val map : MutableMap<String, String>) {
var hoge : String by MapVarProperty(map)
}
fun main(args: Array<String>) {
val mapModel = MapModel(hashMapOf())
mapModel.hoge = "This map key is not null!!!!"
println(mapModel.hoge) // This map key is not null!!!!
}
ReadOnlyPropertyの場合はsetValueの実装が不要で、read onlyなproperty(valで定義されるもの)にしか使えません。
作っておいてなんですが、Mapの値をset/getする機能はKotlin本体に既に組み込まれているので、この場合 by mapとしてやるだけでほぼ同じことができます。
記事内のサンプルコードについて
Githubにアップしました。
Delegationについてもっと知りたい
もっと知りたい方は、公式ドキュメントをどうぞ。
https://kotlinlang.org/docs/reference/delegation.html https://kotlinlang.org/docs/reference/delegated-properties.html
実際にどう使うかは拙作のKoreferenceが参考になると思います。
KoreferenceはAndroidのSharedPreferencesをClass DelegationやDelegated Propertyでよしなに扱うライブラリーです。
皆さんもKotlinのDelegationで楽しい委譲ライフをお過ごしください。
明日の担当は@com4dcさんです。楽しみですね!
おまけ
初めてKotlinに言及したtweet
Kotlin面白そう
— 【最新】設計書_20151201.xls (@takuji31) 2014, 12月 25
思ったより最近だった。