Kotlin Academy · レッスン

型消去とreified型パラメータ

inline関数でreifiedを使い、実行時に型情報へアクセスします。

レッスン 4/414 ステップ

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

型消去とは

実行時、JVMはジェネリック型の型引数を消去します。List<String>もList<Int>も、実際には単なるListです。そのため、ジェネリック型に対する実行時の型操作には制限があります。

fun main() {
    val strings: List<String> = listOf("a", "b")
    val ints: List<Int> = listOf(1, 2)
    println(strings.javaClass == ints.javaClass) // true: both ArrayList
    println(strings is List<*>)  // OK with star
    // println(strings is List<String>) // warning: unchecked cast
}

問題:消去された型はチェックできない

実行時には型情報が失われているため、特定のジェネリック型引数に対してisを使用することはできません。

fun checkType(obj: Any) {
    // This works:
    if (obj is List<*>) println("It's a list of something")
    // This doesn't compile cleanly:
    // if (obj is List<String>) println("It's a list of strings") // unchecked
}
fun main() {
    checkType(listOf("hello"))
    checkType(42)
}

inline関数の復習

inlineは、コンパイル時に関数本体を呼び出し箇所へコピーします。これによりreifiedが可能になります。型引数も呼び出し箇所で置き換えられるためです。

inline fun <T> myRun(block: () -> T): T = block()
fun main() {
    val result = myRun { 42 }
    println(result) // 42
    // The compiler inlines the lambda body — no object allocation
}

reified:実行時のTへのアクセス

inlineとreifiedを組み合わせると、呼び出し箇所で型引数が保持され、Tに対してis、as、::classを使用できるようになります。

inline fun <reified T> isInstance(value: Any): Boolean = value is T
fun main() {
    println(isInstance<String>("hello"))  // true
    println(isInstance<Int>("hello"))     // false
    println(isInstance<List<*>>(listOf())) // true
}

TのKClassを取得する

reified Tを使うと、実行時にT::classを取得できます。通常、ジェネリック関数では不可能な操作です。

inline fun <reified T> classNameOf(): String = T::class.simpleName ?: "Unknown"
fun main() {
    println(classNameOf<String>())   // String
    println(classNameOf<Int>())      // Int
    println(classNameOf<List<*>>())  // List
}

reifiedによる安全なキャスト

reifiedを使って、例外をスローする代わりにnullを返す型安全なキャストユーティリティを作成します。

inline fun <reified T> Any?.safeCast(): T? = this as? T
fun main() {
    val obj: Any = "Hello"
    val s: String? = obj.safeCast<String>()
    val i: Int? = obj.safeCast<Int>()
    println(s) // Hello
    println(i) // null
}

reifiedによるJSON解析

Gsonやkotlinx.serializationなどのライブラリは、Classオブジェクトを明示的に渡さなくても、呼び出し箇所でreifiedを使って対象の型を推論します。

import com.google.gson.Gson
// Simulated without real Gson:
inline fun <reified T> parseJson(json: String): T {
    return Gson().fromJson(json, T::class.java)
}
// Usage:
// val user: User = parseJson<User>("""{"name":"Alice"}""")

filterIsInstanceでのreified

標準ライブラリのfilterIsInstance<T>()はreifiedを使い、手動でキャストせずにリストを特定の型へ絞り込みます。

fun main() {
    val mixed: List<Any> = listOf(1, "hello", 2.5, "world", true)
    val strings = mixed.filterIsInstance<String>()
    val ints = mixed.filterIsInstance<Int>()
    println(strings) // [hello, world]
    println(ints)    // [1]
}

inlineでない関数ではreifiedを使用できない

reifiedはinline関数でのみ使用できます。inlineなしで使用しようとするとコンパイルエラーになります。

// Error:
// fun <reified T> broken(): String = T::class.simpleName ?: "?"

// Must be inline:
inline fun <reified T> correct(): String = T::class.simpleName ?: "?"
fun main() {
    println(correct<Double>()) // Double
}

reifiedとリフレクション

reifiedとリフレクションを組み合わせると、定型的なClassパラメーターを使わずに、型のプロパティやアノテーションを動的に調べられます。

inline fun <reified T : Any> properties(): List<String> =
    T::class.members.map { it.name }
data class Point(val x: Int, val y: Int)
fun main() {
    println(properties<Point>()) // [x, y, component1, component2, copy, equals, hashCode, toString]
}

実践:型安全なサービスロケーター

reifiedを使って、KClassとインスタンスを対応付けるサービスロケーターを構築します。アノテーションプロセッサーを使わない軽量なDIに利用できます。

val registry = mutableMapOf<kotlin.reflect.KClass<*>, Any>()
inline fun <reified T : Any> register(instance: T) { registry[T::class] = instance }
@Suppress("UNCHECKED_CAST")
inline fun <reified T : Any> resolve(): T = registry[T::class] as T
class Logger { fun log(msg: String) = println("[LOG] $msg") }
fun main() {
    register(Logger())
    resolve<Logger>().log("Service located!")
}

reifiedの制限

reified型パラメーターは、inlineでないコンテキストでは使用できません。また、型を後で使用するために保存したり、実行時に型を必要とするinlineでない関数へ渡したりすることもできません。

inline fun <reified T> foo() {
    val clazz = T::class // OK here
    // bar(clazz)        // must pass clazz explicitly to non-inline funs
    println(clazz.simpleName)
}
fun bar(clazz: kotlin.reflect.KClass<*>) = println(clazz.simpleName)
fun main() { foo<String>() }

クイックチェック

型パラメーターをreifiedにするには、何が必要ですか。

まとめ

型消去は、実行時にジェネリック型の情報を削除します。inline関数のreifiedは、呼び出し箇所で型引数を保持し、Tに対するis、as、::class、リフレクションを可能にします。手動でClassオブジェクトを渡す必要はありません。

無料で開始

AI チューターと学ぶ Kotlin — 無料

ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。

コース
51
レッスン
203

よくある質問

「型消去とreified型パラメータ」レッスンは無料ですか?

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

「型消去とreified型パラメータ」で何を学びますか?

inline関数でreifiedを使い、実行時に型情報へアクセスします。 ブラウザで直接実行するハンズオンコードでKotlin Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

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

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

「型消去とreified型パラメータ」レッスンにはどのくらい時間がかかりますか?

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

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

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

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

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