言語
日本語
English

Caution

お使いのブラウザはJavaScriptが無効になっております。
当サイトでは検索などの処理にJavaScriptを使用しています。
より快適にご利用頂くため、JavaScriptを有効にしたうえで当サイトを閲覧することをお勧めいたします。

Kotlin辞典

  1. トップページ
  2. Kotlin辞典
  3. reified 型パラメーター

reified 型パラメーター

対応: Kotlin 1.0(2016)

Kotlinの『reified』型パラメーターは『inline』関数と組み合わせることで、実行時に型情報を保持できます。型消去とは、ジェネリクスの型情報がコンパイル後に失われるJVM上の制約です。通常のジェネリクスではこの型消去によって実行時に型情報が失われますが、『reified』を使うと『T::class』や『is T』が使えます。

構文

// reified 型パラメーターは inline 関数でのみ使えます
inline fun <reified T> 関数名(引数: Any): T? {
    return if (引数 is T) 引数 else null // is T が使えます
}

// T::class で型情報を取得できます
inline fun <reified T> typeNameOf(): String = T::class.simpleName ?: "Unknown"

構文一覧

構文概要
inline fun <reified T>実行時型情報を持つインライン関数を定義します。
is Treified 関数内で型チェックができます。
T::classreified 関数内で KClass を取得できます。
T::class.javareified 関数内で Java の Class オブジェクトを取得できます。
value as? Treified 関数内で安全なキャストができます。

サンプルコード

sample_reified_type_parameter.kt
// リストから指定型の要素だけを取り出します
inline fun <reified T> List<Any>.filterIsInstance(): List<T> {
    return this.filter { it is T }.map { it as T }
}

// 型名を文字列で取得します
inline fun <reified T> typeNameOf(): String = T::class.simpleName ?: "Unknown"

// JSON パース風の実装例(型に応じてパースを分岐)
inline fun <reified T> parseValue(value: String): T? {
    return when (T::class) {
        Int::class    -> value.toIntOrNull() as? T
        Double::class -> value.toDoubleOrNull() as? T
        String::class -> value as? T
        Boolean::class -> value.toBooleanStrictOrNull() as? T
        else          -> null
    }
}

fun main() {
    // filterIsInstance で型を絞り込みます
    val mixed: List<Any> = listOf(1, "hello", 2, "world", 3.0, true)
    val ints: List<Int> = mixed.filterIsInstance()
    println(ints) // [1, 2]

    val strings: List<String> = mixed.filterIsInstance()
    println(strings) // [hello, world]

    // typeNameOf で型名を取得します
    println(typeNameOf<String>()) // String
    println(typeNameOf<Int>()) // Int
    println(typeNameOf<List<*>>()) // List

    // parseValue で型に応じた変換を行います
    val n: Int? = parseValue("42")
    val s: String? = parseValue("hello")
    println(n) // 42
    println(s) // hello
}
reified_type_parameter.kt
kotlinc reified_type_parameter.kt -include-runtime -d reified_type_parameter.jar
java -jar reified_type_parameter.jar
[1, 2]
[hello, world]
String
Int
List
42
hello

よくあるミス

NG例1: inline なしで reified を使おうとする — コンパイルエラーになります

NG例2: reified 型パラメーターをコンストラクタで直接インスタンス化しようとする — reified でもコンストラクタは呼べません

NG例3: 非インライン関数に reified の性質を引き継ごうとする — T::class は渡せますが reified の機能は引き継がれません

正しい書き方: inline + reified をセットで使います

sample_reified_type_parameter_mistakes.kt
inline fun <reified T> getTypeName(): String = T::class.simpleName ?: "Unknown"

fun main() {
    println(getTypeName<Int>()) // Int
    println(getTypeName<String>()) // String
    println(getTypeName<List<*>>()) // List
}

コンパイルして実行すると次のようになります。

kotlinc sample_reified_type_parameter_mistakes.kt -include-runtime -d sample_reified_type_parameter_mistakes.jar
java -jar sample_reified_type_parameter_mistakes.jar
Int
String
List

概要

通常のジェネリクスは JVM の型消去により実行時に型情報が失われます。『reified』と『inline』を組み合わせることで、コンパイラが呼び出し箇所に関数本体をインライン展開し、型情報を保持したコードが生成されます。

Kotlin 標準ライブラリの『filterIsInstance<T>()』や『typeOf<T>()』も同じ仕組みで実装されています。

ジェネリクスの基本はジェネリクス — 基本を、インライン関数の詳細はinline 関数を参照してください。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。