はじめに
業務でKotlinを使用していますが、Kotlin Koansでレシーバ付き関数リテラルの記述を見て「?」となったので理解できるまで調べました。
未来の自分が見ても振り返れるようにまとめます。
?となったコード
以下のコードです。
fun buildString(build: StringBuilder.() -> Unit): String {
val stringBuilder = StringBuilder()
stringBuilder.build()
return stringBuilder.toString()
}
引数でレシーバ付き関数リテラルを使用しています(build: StringBuilder.() -> Unitの部分)。
上記関数を使用する場合、以下のように使用します。
val s = buildString {
this.append("Numbers: ")
for (i in 1..3) {
// 'this' can be omitted
append(i)
}
}
最初はこのコードを見た時点で何が起こっているか理解できませんでした。
理解できなかった点
buildStringの定義内でなぜbuild()とできるのか?
理由は以下の通りです。
-
buildの型がStringBuilder.() -> Unitであり、関数なのでbuild()と呼び出しができる -
() -> Unitの部分は、引数なし、戻り値なしの関数 -
StringBuilder.()の部分は、StringBuilderの引数なしの拡張関数
上記の理解ができたことでこの疑問は解決しました。
使用時の記述が理解できない
buildString {}の中身の処理は理解できますが、なぜbuildString {}の記載のみでいいのかが理解できませんでした。
これは、StringBuilderの引数なしの拡張関数の実装だったことがわかったことで納得できました。
つまり、buildString使用時に実装を記述しているということでした。
なのでbuildString {}の{}内の実装がbuildStringの関数定義内で使用され、結果がsに代入されていることになります。
上記から、共通処理はレシーバ付き関数リテラルを引数に持つ関数で記載し、固有の処理は呼び出し時に記載する、といった使用方法ができると理解しました。
終わりに
今回記載したことが理解できたことで、レシーバ付き関数リテラルの使い所も理解できたと思っています。。。