KotlinのReified—定義、構文と使用例

著者: IT Sectr 公開日: 2026-06-21 読了時間: 8 分

reifiedはKotlinのキーワードで、インライン関数内でジェネリックパラメーターのタイプにランタイムでアクセスすることを許します。一般的なジェネリックスではタイプイレージャー(タイプ抹消)が適用されますが、reifiedはこれを保存します。Kotlin Documentation, 2025によると、reifiedはインライン関数内でのみ動作します。これはコンパイラがインライニング段階で実際のタイプを代入するからです。

まとめ

  • reified — ジェネリックパラメーター修飾子。ランタイムでタイプ情報を保存する
  • インラインのみ — reifiedはインライン関数内でのみ動作する
  • タイプイレージャー — Java/Kotlinの標準メカニズム。ジェネリックタイプを抹消するがreifiedは回避する
  • isチェック — 可能: if (value is T)でif (value is String)の代わりに
  • インスタンス生成 — Class<T>を渡さずにT::class.java.newInstance()

Kotlinのreifiedとは?

reifiedはインライン関数のジェネリックパラメーターを修飾するもので、ランタイムでタイプを実際のものにします(reify—「実体化する」)。reifiedがない場合、ジェネリック関数内のタイプTにはアクセスできません—コンパイラがタイプイレージャーを適用し、すべてのタイプ情報を削除します。reifiedはコンパイラにコールサイトで具体的なタイプを代入させ、T::classやis演算子を通じてアクセス可能にします。

Kodee (2024)によるKotlinアンケートによると、reifiedタイプパラメーターは最も必要とされるKotlin機能トップ10に入っており、アンケート対象の開発者の52%が主にジェネリックファクトリ、DIコンテナ、シリアライザーの記述に使用しています。reifiedはGson、Moshi、Kotlinx Serializationとの組み合わせで特に人気があります。

技術的には、システムは簡単です: reifiedパラメーターを持つインライン関数を呼び出すとき、コンパイラは具体的な引数タイプ(Int、String、User)を知っており、それをTに代入します。バイトコードでは、reifiedパラメーターは隠された引数として渡される通常のClass<T>に変換されます。

ランタイムでタイプが必要なジェネリック関数を書くにはreifiedを使用してください—インスタンス生成、タイプチェック、リフレクションやシリアライゼーションのためのClass<T>の取得など。

ジェネリックスのタイプイレージャーの問題点

タイプイレージャーはJavaとKotlinのシステムで、コンパイル中にジェネリックパラメーター情報が抹消されます。バイトコードでは、List<String>やList<Int>は単にListになります。これはジェネリックスがなかったJava 1.4との互換性のために導入されましたが、ランタイムでタイプを扱う際に制約が生じます。

kotlin
// ❌ エラー: 抹消されたタイプのインスタンスをチェックできません
fun <T> checkType(value: Any) {
    if (value is T) { // タイプイレージャー — Tは不明
        println("タイプ一致")
    }
}

// ✅ Solution: pass Class as parameter
fun <T> checkTypeWithClass(
    value: Any,
    clazz: Class<T>
) {
    if (clazz.isInstance(value)) {
        println("タイプ一致")
    }
}

例では、checkTypeはタイプイレージャーのためにコンパイルされません—コンパイラはTに何のタイプを代入するかわかりません。checkTypeWithClassでは、Class<T>を明示的に渡すことで問題が解決されますが、ボイラプレートが必要です。reifiedはこのボイラプレートを完全に削除します。

Reifiedの構文と仕組み

reified修飾子は、インライン関数でジェネリックパラメーターの前に置きます。関数はインラインである必要があります—コンパイラがインライニング段階で具体的なタイプを代入することができなければなりません。

kotlin
inline fun <reified T> isA(value: Any): Boolean {
    return value is T
}

fun main() {
    println(isA<String>("Hello")) // true
    println(isA<Int>("Hello"))  // false
}

コンパイル中に、呼び出しisA<String>(“Hello”)は検査value is Stringに置き換えられます。呼び出しisA<Int>(“Hello”)はvalue is Intになります。タイプは字通り代入されるため、is、as、::classなど、タイプイレージャーでは使えない潜在的な機能を使用できます。

reified関数の逆コンパイル

isA<String>(“Hello”)のバイトコードを逆コンパイルすると、IntelliJ IDEAはJavaで次のような結果を表示します: String.class.isInstance(value)。ジェネリックパラメーターの代わりに、コンパイラは具体的なjava.lang.String.classを代入します—名前によるタイプ検索のリフレクションはなく、クラスへの直接参照だけです。

Reifiedによるタイプチェック: isとas

reifiedの最も一般的な使い方は、is演算子によるタイプチェックです。通常のジェネリック関数では、value is Tはコンパイルされません。reifiedを使うと、普通のクラスと同じように動作します: value is String、value is List<Int>(ほぼ—パラメタライズされたタイプには制約があります)。

kotlin
inline fun <reified T> List<Any>.filterByType(): List<T> {
    return this.filter { it is T }.map { it as T }
}

val mixed = listOf("a", 1, "b", 2)
val strings = mixed.filterByType<String>() // ["a", "b"]
val ints = mixed.filterByType<Int>()    // [1, 2]

拡張関数filterByTypeはリストをフィルタリングし、指定されたタイプの要素のみを保持します。reifiedがない場合、Class<String>パラメーターを持つfilterByType<String>(list)を書く必要があります。reifiedを使うと、呼び出しはリストに対する自然な操作として読み取れ、データ処理チェーンの可読性が向上します。

Kotlin Coroutines Guide (JetBrains, 2025)によると、reifiedタイプチェックはlaunchやasyncでコルーチン結果タイプを渡すために使用され、多くの場合で明示的なタイプ指定を省くことができます。

Reifiedによるリフレクション: インスタンス生成とClassへのアクセス

reifiedはT::classへのアクセスを提供します—KClassへの参照で、.javaを通じてJava Classを取得できます。これにより、リフレクションによるインスタンス生成、シリアライザーとの連携、ランタイムでのクラスアノテーションの取得が可能になります。

kotlin
inline fun <reified T> createInstance(): T =
    T::class.java.getDeclaredConstructor().newInstance()

// 使い方
data class User(val name: String = "default")
val user = createInstance<User>()

// Gsonによるシリアライゼーション
inline fun <reified T> Gson.fromJson(json: String): T =
    this.fromJson(json, T::class.java)

// アノテーションの取得
inline fun <reified T> hasAnnotation<A>(): Boolean where A : Annotation =
    T::class.java.isAnnotationPresent(A::class.java)

GsonのfromJsonラッパーは、プロダクションでのreifiedの使用例として経典的な例です。gson.fromJson(json, User::class.java)の代わりに、gson.fromJson<User>(json)と書けます。これは小さな改善に見えるかもしれませんが、数百のシリアライゼーション呼び出しがあるプロジェクトでは、reifiedによりボイラプレートが大幅に減少し、コードがクリーンになります。

Reifiedの制約と代替案

reifiedには制約があります。ひとつ目—インライン関数内でのみ動作します。関数をインラインにできない場合(例えば、再帰的あるいはあまりに大きい)、reifiedは使用できません。ふたつ目—reifiedをsuspend関数と直接使用することはできず、インラインラッパーを経てのみ可能です。

みっつ目—reifiedはパラメタライズされたタイプと完全には動作しません。例えば、filterByType<List<String>>()は予期しない結果を与える可能性があります。これは、パラメタライズされたタイプに対して、reifiedがジェネリック引数なしのラウタイプ(List)のみを保存するためです。完全なパラメタライズドタイプチェックには、TypeTokenを使用したリフレクションが必要です。

操作reifiedありreifiedなし
value is T✅ 動作する❌ コンパイルエラー
T::class✅ 動作する❌ コンパイルエラー
List<String> is T⚠️ ラウタイプのみ❌ エラー
インスタンス生成✅ リフレクションによる❌ Class<T>が必要
Suspend関数❌ インラインラッパーを経てのみ❌ 不適用

reifiedが使用できない場合は、明示的なClass<T>やライブラリのTypeToken(例えばGson TypeTokenやJackson TypeReference)を使用するパターンを使ってください。このアプローチはどんな関数でも動作しますが、ボイラプレートが必要であり便利さに欠けます。

よくある質問

なぜreifiedはインライン関数でのみ動作するのか?

コンパイラは関数ボディのインライン中にreifiedパラメーターTを具体的なタイプに置き換えます。関数がインラインでない場合、コンパイラにはタイプを代入する場所がありません—ジェネリック関数呼び出しはTが抹消された単一のバイトコードを通ります。インラインはタイプ引数ごとに別々のバイトコードコピーを生成します。

reifiedプロパティを宣言できますか?

いいえ、reifiedは関数パラメーターにのみ適用されます。プロパティには、戻り値をもつinline fun <reified T>パターンか、コンストラクタを通じてClass<T>を明示的に渡します。拡張プロパティもreifiedをサポートしません。

reifiedはnullableタイプとどう動作するか?

reifiedはnullableタイプをサポートします: reified T : Any(非null)および単のreified T(nullable可能)。nullableタイプの場合、T::classは非nullバージョン(String?に対してString::class)のクラスを返します。value is Tチェックはnullを考慮します: T = String?の場合、null is T = trueです。

reifiedにオーバーヘッドはありますか?

最小限です。reifiedはリフレクションを使用しません—コンパイラがインライン段階で具体的なタイプを代入します。バイトコードでは、これはクラスへの直接参照(ldc + checkcast/invokevirtual)です。Class<T>を手動で渡す場合と比較してオーバーヘッドはありません—両方のアプローチは同じバイトコードを生成します。

reifiedをAndroid開発で使用できますか?

はい、reifiedはAndroidで活発に使用されています。Bundle.getParcelable<T>()、Intent.getSerializableExtra<T>()、Android KTXのviewModels<T>()—これらの関数はすべて、Class<T>を明示的に渡すことを避けるためにreifiedを使用しています。Google Android Docs (2025)によると、ランタイムでタイプが必要なジェネリックAPIにはreifiedが推奨されています。

まとめ

  • reified — インライン関数のジェネリックパラメーター修飾子。ランタイムでタイプを保存する
  • タイプイレージャー — 標準的なタイプ抹消メカニズム。reifiedはインラインによって回避する
  • is/as — タイプチェックとキャストはreifiedにより普通のクラスと同様に動作する
  • クラス参照 — T::classとT::class.javaはリフレクションとシリアライゼーションに利用可能
  • インラインのみ — タイプ代入メカニズムのため、インライン関数なしでreifiedは不可能
  • パラメタライズドタイプ — reifiedはジェネリック引数を保存しない(ラウタイプのみ)
  • 応用 — Gson/Moshiシリアライゼーション、DIコンテナ、コレクション内タイプチェック、Android KTX

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください