Kotlin KoansでKotlinを勉強したメモをまとめています。
問題
リンクは以下。
https://play.kotlinlang.org/koans/Generics/Generic%20functions/Task.kt
少し簡略化して説明すると、以下のコードが動くように、リストに入った要素を条件によって2つに分けるpartitionTo()
メソッドを実装するというものです。
val (words, lines) = listOf("a", "a b", "c", "d e") .partitionTo(ArrayList(), ArrayList()) { s -> !s.contains(" ") } check(words == listOf("a", "c")) check(lines == listOf("a b", "d e"))
解答と解説
まず、解答例は以下です。
fun <T, C : MutableCollection<T>> Collection<T>.partitionTo(first: C, second: C, predicate: (T) -> Boolean): Pair<C, C> { for (element in this) { if (predicate(element)) { first.add(element) } else { second.add(element) } } return Pair(first, second) }
このコードはジェネリックを学習するのが目的ですが、Kotlinの初心者からすると何点かポイントがあるので解説します。
1. 拡張関数について
Kotlin Koansにも登場しますが、Kotlinには拡張関数という機能があり、別のクラスにメソッドを追加することができます。
この解答例では、Collection
クラスにpartitonTo
メソッドを追加しています。
2. ジェネリックについて
partitionTo()
は3つの引数を受け取ります。
始めの2つはArrayList
などの配列型で、その要素の型は何でもOKです。
3つめの要素は関数ですが、その引数は↑の要素の方と同じである必要があります。
つまりpartitionTo()
の定義の中で、型が一位に定まらないものが2つあります。そのため、以下のように記述する必要があります。
<T, C>
さらに、TはpartitionTo()
の第1・2引数で使われ、Cはその要素、および第3引数の関数の引数です。
TがCのコレクション型であることを明記するため
<T, C : MutableCollection<T>>
という記述をしています。
3. 引数の関数を()の外に出す
partitionTo
を実行するコードで
partitionTo(ArrayList(), ArrayList()) { s -> !s.contains(" ") }
のように書いていましたが、これは
partitionTo(ArrayList(), ArrayList(), { s -> !s.contains(" ") } )
のように書くのと同じです。
これは、引数の最後が関数の場合にのみできる書き方です。
ちなみに、関数を引数に渡す書き方はとっつきにくいですが、自分は以下のサイトが分かりやすいなと思いました。 https://pouhon.net/kotlin-higher-order-function/1342/