はじめに
業務で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
に代入されていることになります。
上記から、共通処理はレシーバ付き関数リテラルを引数に持つ関数で記載し、固有の処理は呼び出し時に記載する、といった使用方法ができると理解しました。
終わりに
今回記載したことが理解できたことで、レシーバ付き関数リテラルの使い所も理解できたと思っています。。。