0Pricing
Kotlin Academy · レッスン

whereによるジェネリック関数と型制約

単一および複数の型制約を持つジェネリック関数を記述します。

「whereによるジェネリック関数と型制約」はCoddyKit上の無料Kotlin Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはKotlin Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Kotlin Academyコースには全4レッスンが含まれています。

ジェネリック関数の基本

ジェネリック関数では、関数名の前に山かっこで囲んだ型パラメーターを記述します。型パラメーターは、パラメーターの型や戻り値の型に使用できます。

fun <T> identity(value: T): T = value
fun <T> listOf2(a: T, b: T): List<T> = listOf(a, b)
fun main() {
    println(identity(42))       // 42
    println(identity("hello"))  // hello
    println(listOf2(1, 2))      // [1, 2]
}

<code>:</code>制約による上限境界

:を使った上限境界により、型パラメーターに制約を設定できます。型は、境界型のサブタイプでなければなりません。

fun <T : Comparable<T>> max(a: T, b: T): T = if (a >= b) a else b
fun main() {
    println(max(3, 7))          // 7
    println(max("apple", "banana")) // banana
    // max(listOf(1), listOf(2)) // error: List is not Comparable
}

Nullableの上限境界

デフォルトの上限境界はAny?(nullable)です。nullを除外するにはT : Anyを使用します。

fun <T : Any> notNull(value: T?): T {
    return value ?: throw IllegalArgumentException("Value is null")
}
fun main() {
    println(notNull("hello"))  // hello
    try { notNull(null) } catch (e: Exception) { println(e.message) }
}

whereによる複数の制約

where句を使うと、1つの型パラメーターに複数の制約を適用できます。

fun <T> process(item: T): String
        where T : Comparable<T>, T : CharSequence {
    return "Length=${item.length}, sorted=${item > "a"}"
}
fun main() {
    println(process("kotlin")) // Length=6, sorted=true
    // process(42) // error: Int doesn't implement CharSequence
}

クラスでのwhere

where句は、クラスレベルの型パラメーターにも使用できます。

class SortedContainer<T>(private val items: MutableList<T> = mutableListOf())
        where T : Comparable<T>, T : Any {
    fun add(item: T) { items.add(item); items.sort() }
    fun top(): T? = items.lastOrNull()
}
fun main() {
    val c = SortedContainer<Int>()
    c.add(5); c.add(2); c.add(8)
    println(c.top()) // 8
}

ジェネリック拡張関数

拡張関数にも型パラメーターを指定できるため、特定のジェネリック型で利用できるようになります。

fun <T : Comparable<T>> List<T>.second(): T? {
    return if (size >= 2) this[1] else null
}
fun main() {
    println(listOf(10, 20, 30).second()) // 20
    println(listOf("a").second())        // null
}

戻り値における型パラメーター

ジェネリック関数はコンテキストから型を推論できるため、呼び出し側で型引数を明示する必要がありません。

fun <T> MutableList<T>.popOrDefault(default: T): T {
    return if (isEmpty()) default else removeAt(lastIndex)
}
fun main() {
    val list = mutableListOf(1, 2, 3)
    println(list.popOrDefault(0)) // 3
    println(list.popOrDefault(0)) // 2
    println(mutableListOf<Int>().popOrDefault(-1)) // -1
}

再利用可能なジェネリックユーティリティ

ジェネリック関数を使うと、重複なしでさまざまな型に対応できる再利用可能なユーティリティ関数を作成できます。

fun <T> Iterable<T>.firstOrElse(default: T): T = firstOrNull() ?: default
fun <T, R : Comparable<R>> Iterable<T>.maxByOrElse(selector: (T) -> R, default: T): T =
    maxByOrNull(selector) ?: default
fun main() {
    println(listOf<String>().firstOrElse("fallback")) // fallback
    println(listOf("a","bbb","cc").maxByOrElse({ it.length }, "")) // bbb
}

スター投影と境界付きワイルドカード

境界付き型パラメーター(Tの生成または消費が必要な場合に便利)と、スター投影(型が不明な場合に使用)の違いを確認します。

fun <T : Number> sumList(list: List<T>): Double =
    list.sumOf { it.toDouble() }
// Star projection: read-only, type unknown
fun printAll(list: List<*>) = list.forEach { println(it) }
fun main() {
    println(sumList(listOf(1, 2, 3)))    // 6.0
    printAll(listOf("a", 1, true))       // a, 1, true
}

ジェネリックなPairの入れ替え

Pairの要素を逆順にする、シンプルですが分かりやすいジェネリック関数です。

fun <A, B> Pair<A, B>.swap(): Pair<B, A> = Pair(second, first)
fun main() {
    val pair = Pair("hello", 42)
    val swapped = pair.swap()
    println(swapped) // (42, hello)
}

実践:ジェネリックなResultの展開

境界付きジェネリックを使って、型付き変換によりResult<T>を変換するユーティリティを作成します。

fun <T : Any, R : Any> Result<T>.mapNotNull(transform: (T) -> R?): Result<R> {
    return fold(
        onSuccess = { v ->
            val r = transform(v)
            if (r != null) Result.success(r) else Result.failure(NoSuchElementException())
        },
        onFailure = { Result.failure(it) }
    )
}
fun main() {
    val r = Result.success("123")
    val n = r.mapNotNull { it.toIntOrNull() }
    println(n) // Success(123)
}

クイックチェック

1つの型パラメーターに複数の制約を適用するには、どうすればよいですか。

まとめ

ジェネリック関数は型パラメーターを使って、再利用可能で型安全なコードを記述します。単一の境界には:、複数の境界にはwhereを使って制約します。Kotlinコンパイラーは、呼び出し側で制約を適用します。

よくある質問

「whereによるジェネリック関数と型制約」レッスンは無料ですか?

はい。「whereによるジェネリック関数と型制約」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Kotlin Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Kotlin Academyコースには全4レッスンが含まれています。

「whereによるジェネリック関数と型制約」で何を学びますか?

単一および複数の型制約を持つジェネリック関数を記述します。 ブラウザで直接実行するハンズオンコードでKotlin Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Kotlin Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのKotlin Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。

「whereによるジェネリック関数と型制約」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このKotlin Academyレッスンでコードを書いて実行できますか?

はい。すべてのKotlin Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. whereによるジェネリック関数と型制約
  2. 宣言側変性:inとout
  3. Star Projectionと*の使いどころ
  4. 型消去とreified型パラメータ
← Kotlin Academyに戻る