Menu
Coddy logo textTech

ジェネリック関数

CoddyのKotlinジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 37/57。

ジェネリック関数は、あらゆる型で動作します。その型パラメーターは、関数名の前に山括弧で記述され、呼び出し元が使用する型を表します。Tが通常使われる名前です。

fun <T> firstOr(list: List<T>, default: T): T = if (list.isEmpty()) default else list[0]

mainの内部では:

println(firstOr(listOf(3, 5), 0) + 1)
println(firstOr(listOf<String>(), "none").uppercase())

出力:

4
NONE

Kotlinは引数から型引数を推論します。推論する対象が何もない場合は、上記のlistOf<String>()のように、自分で記述します。

fun <T> repeatItem(item: T, times: Int): List<T> = List(times) { item }

mainの中では:

println(repeatItem("ab", 3))
println(repeatItem(7, 2))
val empty = repeatItem<Double>(1.5, 0)
println(empty)

出力:

[ab, ab, ab]
[7, 7]
[]

関数には複数の型パラメーターを指定でき、関数型パラメーターではそれらを使用できます。これは、ライブラリ関数である map が、あらゆる要素型と結果型に対して動作する仕組みです。

fun <A, B> transform(items: List<A>, f: (A) -> B): List<B> {
    val out = mutableListOf<B>()
    for (item in items) out.add(f(item))
    return out
}

fun <K, V> invert(map: Map<K, V>): Map<V, K> = map.entries.associate { (k, v) -> v to k }

main の中で:

println(transform(listOf("kiwi", "fig")) { it.length })
println(invert(mapOf("a" to 1, "b" to 2)))

出力:

[4, 3]
{1=a, 2=b}

型パラメーターは呼び出し元の型を保持します。firstOr(listOf(3, 5), 0) は Int を返すため、+ 1 はコンパイルできます。List<Any> 用に記述したバージョンもすべてのリストを受け入れますが、Any を返すため、呼び出すたびにキャストが必要になります。

fun firstAny(list: List<Any>, default: Any): Any = if (list.isEmpty()) default else list[0]
// firstAny(listOf(3, 5), 0) + 1               // エラー: Any に plus がない
(firstAny(listOf(3, 5), 0) as Int) + 1         // コンパイルできるが、キャストが必要
firstOr(listOf(3, 5), 0) + 1                   // ジェネリック: 結果は Int
challenge icon

チャレンジ

簡単

任意の型のリストで動作する、2つの汎用関数を記述してください。countWhere(items, predicate)は、述語である関数(T) -> Booleanを満たす要素の数を返します。mostCommon(items)は、最も多く出現する要素(出現回数が同じ場合は、リスト内で最初に現れる要素)を返します。空のリストの場合はnullを返します。

提供されたコードは、単語の行と数値の行を読み取り、3文字より長い単語の数、最も頻出する単語、偶数の数、最も頻出する数、そして空のリストに対してnothing: nullを出力します。

コードはListTools.ktに記述してください。Main.ktには提供された入力/出力コードが含まれており、編集できません。

自分で試してみよう

fun main() {
    // 提供された入出力コード: そのままにしておく
    val input = generateSequence(::readLine).toList()
    val words = input[0].split(" ")
    val numbers = input[1].split(" ").map { it.toInt() }
    println("long words: ${countWhere(words) { it.length > 3 }}")
    println("most common word: ${mostCommon(words)}")
    println("even numbers: ${countWhere(numbers) { it % 2 == 0 }}")
    println("most common number: ${mostCommon(numbers)}")
    println("nothing: ${mostCommon(listOf<String>())}")
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Kotlinプレイグラウンド