Webエンジニアのメモ帳

技術的な話を中心に書いています。

【Kotlin Koans 解説】ジェネリック (Generics)

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/