はじめに
公式の問題集「Kotlin Koans」を解きながらKotlinを学習します。
過去記事はこちら
- Introduction
- Classes
- Conventions
- Collections
- Properties
問題
デリゲートプロパティについて学び、デリゲートを使ってプロパティを遅延させます。
class LazyProperty(val initializer: () -> Int) {
val lazyValue: Int by TODO()
}
問題のポイント
一般的なプロパティの中には、必要な都度手動で実装してもよいものがありますが、一度実装してライブラリに追加し、後で再利用する方が便利です。例えば
- レイジープロパティ:最初のアクセス時のみ値が計算される。
- Observableプロパティ:このプロパティの変更についてリスナーに通知される。
- 各プロパティに対して個別のフィールドを用意するのではなく、マップにプロパティを格納する。
これらの(そして他の)ケースをカバーするために、Kotlinはデリゲートプロパティをサポートしています。
class Example {
var p: String by Delegate()
}
構文としては、val/var <プロパティ名>:<Type> by <expression>
です。
by
の後の式はデリゲートです。なぜなら、プロパティに対応するget()
とset()
は、そのgetValue()
とsetValue()
メソッドにデリゲートされることになるからです。
プロパティのデリゲートはインターフェースを実装する必要はありませんが、getValue()
(vars
の場合は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.")
}
}
Delegate
のインスタンスに委譲されたp
から読み込むとき、Delegate
のgetValue()
関数が呼び出されます。
その第一引数にはp
を読み込んだオブジェクトが、第二引数にはp
自身の説明(例えば、名前を取る)が入ります。
val e = Example()
println(e.p) // Example@33a17727, thank you for delegating 'p' to me!
同様に、p
に代入する場合は、setValue()
関数が呼び出されます。
最初の2つのパラメータは同じもので、3つ目のパラメータには代入される値が格納されます。
e.p = "NEW"
// NEW has been assigned to 'p' in Example@33a17727.
Lazy properties
Kotlinの標準ライブラリは、いくつかの便利な種類のデリゲートのためのファクトリーメソッドを提供します。
lazy() はラムダを受け取り、Lazy のインスタンスを返す関数で、遅延プロパティを実装するためのデリゲートとして使用することができます。
get() の最初の呼び出しは、lazy() に渡されたラムダを実行し、その結果を記憶しています。それ以降の get() の呼び出しは、単に記憶された結果を返します。
val lazyValue: String by lazy {
println("computed!")
"Hello"
}
fun main() {
println(lazyValue)
println(lazyValue)
}
// computed!
// Hello
// Hello
解答例
class LazyProperty(val initializer: () -> Int) {
val lazyValue: Int by lazy(initializer)
}