0Pricing
Kotlin Academy · レッスン

Star Projectionと*の使いどころ

不明なジェネリック型にStar Projectionを適用し、その制限を理解します。

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

スター投影とは

スター投影(*)は、「型引数が不明なジェネリック型」を表すKotlinの方法です。Javaのraw型や、境界のないワイルドカード<?>に相当する安全な仕組みです。

fun printList(list: List<*>) {
    for (item in list) println(item)
}
fun main() {
    printList(listOf(1, 2, 3))
    printList(listOf("a", "b"))
    printList(listOf(true, 42, "mixed"))
}

スター投影された型からの読み取り

List<*>から要素を読み取ると、Any?として扱えます。実際の型が不明なため、要素を追加することはできません。

fun process(list: List<*>) {
    val first: Any? = list.firstOrNull()
    println("First: $first, Size: ${list.size}")
    // list.add("x") // compile error: can't write to List<*>
}
fun main() {
    process(listOf(10, 20, 30))
}

スター投影とout Anyの違い

List<*>とList<out Any?>は同等です。スター投影は、上限境界を持つ共変投影の省略記法です。

fun sumStars(list: List<*>): Int = list.size
fun sumOut(list: List<out Any?>): Int = list.size
fun main() {
    val l = listOf(1, "two", true)
    println(sumStars(l))  // 3
    println(sumOut(l))    // 3
}

関数パラメーターでのスター投影

ジェネリック型の任意のバリエーションを受け入れつつ、Tを知る必要のないメソッドだけを呼び出す場合は、関数シグネチャーで*を使用します。

fun containerInfo(map: Map<*, *>): String {
    return "keys=${map.keys.size}, values=${map.values.size}"
}
fun main() {
    val m = mapOf(1 to "one", 2 to "two")
    println(containerInfo(m))
}

MutableListでのスター投影

MutableList<*>では、Any?として読み取れますが、まったく書き込めません。コンパイラーによって防止されます。

fun clearFirstElement(list: MutableList<*>) {
    if (list.isNotEmpty()) {
        list.removeAt(0)  // OK: remove by index doesn't need T
        // list.add("x") // Error: can't write unknown type
    }
}
fun main() {
    val m = mutableListOf(1, 2, 3)
    clearFirstElement(m)
    println(m) // [2, 3]
}

スター投影を使用したクラス

実行時にジェネリッククラスの型を確認するときは、型引数が消去されるためスター投影を使用します。

fun isListOfSomething(obj: Any): Boolean {
    return obj is List<*>  // OK with star
    // obj is List<String> // Warning: cannot check erased type
}
fun main() {
    println(isListOfSomething(listOf(1, 2)))  // true
    println(isListOfSomething("not a list"))  // false
}

複数のスター型パラメーター

不明な型のPairやMapを扱う場合、関数に複数のスター投影されたパラメーターを指定できます。

fun describeMap(map: Map<*, *>) {
    map.forEach { (k, v) ->
        println("${k?.javaClass?.simpleName} -> ${v?.javaClass?.simpleName}")
    }
}
fun main() {
    describeMap(mapOf("key" to 42, 1 to true))
}

スター投影を使わない場合

コレクションへの書き込みが必要な場合や、代わりに境界付き型パラメーターを使える場合は、スター投影を避けてください。境界付き型パラメーターのほうが多くの情報を提供できます。

// Bad: loses type information
fun addToStar(list: MutableList<*>, item: Any?) {
    // list.add(item) // compile error
}
// Better: use bounded type parameter
fun <T : Any> addTyped(list: MutableList<T>, item: T) {
    list.add(item)
}
fun main() {
    val l = mutableListOf("a")
    addTyped(l, "b")
    println(l)
}

型チェックとキャストでのスター投影

スター投影を使ってジェネリッククラスに対するinstanceofチェックを行い、その後、実際の処理のために特定の型へキャストします。

fun handleResponse(response: Any) {
    if (response is List<*>) {
        val list = response as List<*>
        println("List with ${list.size} elements")
        val first = list.firstOrNull()
        if (first is String) println("First string: $first")
    }
}
fun main() {
    handleResponse(listOf("hello", "world"))
    handleResponse(42)
}

実践:ジェネリックイベントディスパッチャー

任意のHandler<*>を受け入れながら、型付きイベントを安全にディスパッチするディスパッチャーです。

interface Handler<in T> { fun handle(event: T) }
class LogHandler : Handler<Any> {
    override fun handle(event: Any) = println("Event: $event")
}
fun dispatch(handler: Handler<*>, event: Any) {
    @Suppress("UNCHECKED_CAST")
    (handler as Handler<Any>).handle(event)
}
fun main() {
    dispatch(LogHandler(), "click")
    dispatch(LogHandler(), 42)
}

reifiedコンテキストでのスター投影

スター投影は、KClass<*>を使って実行時に型を調べるリフレクションやシリアライズのコードで特によく使用されます。

fun printClassName(klass: kotlin.reflect.KClass<*>) {
    println(klass.simpleName)
}
fun main() {
    printClassName(String::class)     // String
    printClassName(List::class)       // List
    printClassName(Int::class)        // Int
}

クイックチェック

List<*>から読み取ると、どの型になりますか。

まとめ

スター投影(*)は、読み取り専用アクセスで任意のジェネリック型のバリエーションを受け入れます。型チェック、ジェネリックユーティリティ、リフレクションに使用します。コレクションへの書き込みが必要な場合や、境界付き型パラメーターのほうが高い型安全性を提供できる場合は避けてください。

よくある質問

「Star Projectionと*の使いどころ」レッスンは無料ですか?

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

「Star Projectionと*の使いどころ」で何を学びますか?

不明なジェネリック型にStar Projectionを適用し、その制限を理解します。 ブラウザで直接実行するハンズオンコードでKotlin Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

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

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

「Star Projectionと*の使いどころ」レッスンにはどのくらい時間がかかりますか?

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

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

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

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

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