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 T | reified 関数内で型チェックができます。 |
| T::class | reified 関数内で KClass を取得できます。 |
| T::class.java | reified 関数内で Java の Class オブジェクトを取得できます。 |
| value as? T | reified 関数内で安全なキャストができます。 |
サンプルコード
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 関数を参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。