問題の歴史:
JVMでは実行中にジェネリクスのパラメータに関する情報は存在しません(型消去)。Kotlinは、インライン関数のために再ifiedジェネリクスパラメータのメカニズムを提案し、Class<T>を渡すような回避策なしで実行時に型Tに関する情報にアクセスできるようにしました。これはKotlinの最も強力な手段の1つです。
問題:
型T(ジェネリック)の値を受け取り、パラメータの型に応じて異なる操作を実行する関数を書かなければなりません。java.lang.Classを明示的に渡すことなく、Javaのリフレクションを使用せずに実行します。クラシカルな型消去では実行時に型Tを知ることができません。
解決策:
Kotlinではインライン関数として再ified修飾子を持つジェネリクスパラメータを宣言することが許可されており、関数の本体内で型Tを「キャプチャ」し、通常の型として扱い、型チェックを行い、リフレクションを通じてインスタンスを作成できます。
コードの例:
inline fun <reified T> isOfType(value: Any): Boolean { return value is T } isOfType<String>(123) // false isOfType<Int>(123) // true
主な特徴:
インライン関数以外で再ifiedを使用できますか?
いいえ!再ifiedジェネリクスパラメータを持つことができるのはインライン関数(およびインライン遅延プロパティ)のみです。理由は、呼び出し場所でのコードの置換えにより、Tの代わりに具体的な型を「置き換える」ことができるからです。
コードの例:
// コンパイルエラー: fun <reified T> errorFun() { } // 正しいバージョン: inline fun <reified T> okFun() { }
インライン再ified関数内でClass<T>を取得できますか?
はい!T::class.javaまたはT::classを書くことで簡単に取得できます。これは、ジェネリックファクトリーやパーサーの作成、リフレクションAPIとの作業に非常に便利です。
コードの例:
inline fun <reified T> printType() { println(T::class.java) } printType<String>() // class java.lang.String
再ified関数内でコンストラクタを介して型Tのインスタンスを作成できますか?
部分的に可能です。リフレクションを使用することはできますが、C++やC#のようにnew T()を直接行うことはできません:
inline fun <reified T : Any> makeInstance(): T? { return T::class.constructors.firstOrNull()?.call() }
ただし、このアプローチでは型Tにパラメータなしのコンストラクタが必要です。
重いリフレクション操作を呼び出す再ified関数:
inline fun <reified T> foo(): T? = T::class.constructors.firstOrNull()?.call()
長所:
短所:
汎用的な型チェックやsafeCastのための再ifiedの使用:
inline fun <reified T> safeCast(value: Any?): T? = value as? T
長所:
短所: