[毎日Kotlin] Day38. String and map builders
はじめに
毎日Kotlinシリーズです。
このシリーズを初めての方はこちらです。「毎日Kotlin」はじめました | Developers.IO
問題
String and map builders | Try Kotlin
Extension function literals are very useful for creating builders, e.g.:
fun buildString(build: StringBuilder.() -> Unit): String { val stringBuilder = StringBuilder() stringBuilder.build() return stringBuilder.toString() } val s = buildString { this.append("Numbers: ") for (i in 1..3) { // 'this' can be omitted append(i) } } s == "Numbers: 123"
Add and implement the function 'buildMap' with one parameter (of type extension function) creating a new HashMap, building it and returning it as a result. The usage of this function is shown below.
狙い
ここで考えて欲しい問題の意図はなんだろうか?
Builderでよく使うパターンですが、見慣れないとややこしく見えてしまうかもしれません。
thisがなんだったか思い出して解いてみよう。
[毎日Kotlin] Day10. Extension functions(拡張関数)
解答例
fun <K, V> buildMap(build: HashMap<K, V>.() -> Unit): Map<K, V> { val map = HashMap<K, V>() map.build() return map }
buildMapの引数がbuild: HashMap<K, V>.() -> Unitになっています。buildMap内で使用する拡張関数buildと考えるとわかりやすいです。
使用例をみると、一時的な拡張関数とみれば、thisがHashMap<K, V>であることがわかりますね。HashMap<K, V>のメンバ関数であるputが使用できます。
fun usage(): Map<Int, String> { return buildMap { put(0, "0") // this.putと同じ for (i in 1..10) { put(i, "$i") // this.putと同じ } } }
別解: apply
Kotlinではちょっと便利なスコープ関数というあります。こちらを使う場面は沢山あります。詳細は割愛し、すでに良記事がありますので、ぜひこちらを参照。
今回の例でもつかえます。applyを使うのがよいと思います。
fun <K, V> buildMap(build: HashMap<K, V>.() -> Unit): Map<K, V> = HashMap<K, V>().apply(build)
apply at 1.1.0 · JetBrains/kotlin
@kotlin.internal.InlineOnly public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
別解: ラムダ式
通常のラムダ式でも同様のことできます。ラムダ式の場合は、引数として渡してあげればよいですね。
fun <K, V> buildMap(build: (HashMap<K, V>) -> Unit): Map<K, V> { val map = HashMap<K, V>() build(map) return map }
ただし、この場合は、HashMap<K, V>は、レシーバーから引数になったので使用の仕方が異なります。
fun usage(): Map<Int, String> { return buildMap { it.put(0, "0") // thisではなくit for (i in 1..10) { it.put(i, "$i") // thisではなくit } } }
しかし、(HashMap<K, V>) -> UnitとHashMap<K, V>.() -> UnitはKotlin上では等価です。
つまり、以下のこともできます。
fun <K, V> buildMap(build: (HashMap<K, V>) -> Unit): Map<K, V> = HashMap<K, V>().apply(build)
あとがき
Day39.でまたお会いしましょう。