web-dev-qa-db-ja.com

Kotlinの具体化キーワードはどのように機能しますか?

reifiedキーワードの目的を理解しようとしています。明らかに ジェネリックのリフレクションが可能になっています

しかし、私がそれを省くとき、それは同じようにうまく働きます。これが実際に差異になるときを説明したい人はいますか?

74
hl3mukkel

TL; DR:reifiedname__の利点

fun <T> myGenericFun(c: Class<T>) 

myGenericFunname__のような汎用関数の本体では、Tname__型にアクセスできません。これは、はコンパイル時にのみ使用可能ですが、実行時には erased _したがって、汎用タイプを関数本体の通常のクラスとして使用する場合は、myGenericFunname__に示すように、クラスをパラメーターとして明示的に渡す必要があります。

ただし、reifiedinlinename__を使用してTname__関数を作成すると、実行時にもTname__の型にアクセスできるため、Class<T>を追加で渡す必要はありません。 Tname__を通常のクラスであるかのように操作できます。変数がインスタンスのTname__であるかどうかを確認することをお勧めします。これはmyVar is Tです。

そのようなinlinename__型reifiedname__を持つTname__関数は、次のようになります。

inline fun <reified T> myGenericFun()

reifiedname__の仕組み

reifiedname__は、 inlinename__関数 と組み合わせてのみ使用できます。このような関数は、コンパイラを関数のバイトコードを、関数が使用されているすべての場所にコピーします(関数は「インライン化」されます)。具体化された型でインライン関数を呼び出すと、コンパイラは型引数として使用される実際の型は、対応するクラスを直接使用するように生成されたバイトコードを変更するため、myVar is Tのような呼び出しは、バイトコードおよび実行時にmyVar is Stringになります(型引数がStringname__の場合)。


reifiedname__がどれほど役立つかを示す例を見てみましょう。 Stringname__と呼ばれるtoKotlinObjectname__の拡張関数を作成して、関数のジェネリック型Tname__で指定された型を持つJSON文字列をプレーンなKotlinオブジェクトに変換しようとします。これには com.fasterxml.jackson.module.kotlin を使用できます。最初のアプローチは次のとおりです。

a)具体化されていないタイプの最初のアプローチ

fun <T> String.toKotlinObject(): T {
      val mapper = jacksonObjectMapper()
                                                        //does not compile!
      return mapper.readValue(this, T::class.Java)
}

readValuename__メソッドは、JsonObjectname__を解析することになっている型を取ります。型パラメーターClassname__のTname__を取得しようとすると、コンパイラーは次のように文句を言います。「「T」を具体化された型パラメーターとして使用できません。代わりにクラスを使用してください。」

b)明示的なClassname__パラメーターの回避策

fun <T: Any> String.toKotlinObject(c: KClass<T>): T {
    val mapper = jacksonObjectMapper()
    return mapper.readValue(this, c.Java)
}

回避策として、Classname__のTname__をメソッドパラメーターにして、readValuename__の引数として使用できます。これは機能し、一般的なJavaコードの一般的なパターンです。次のように呼び出すことができます。

data class MyJsonType(val name: String)

val json = """{"name":"example"}"""
json.toKotlinObject(MyJsonType::class)

c)Kotlinの方法:reifiedname__

inlinename__型パラメーターreifiedname__でTname__関数を使用すると、関数を異なる方法で実装できます。

inline fun <reified T: Any> String.toKotlinObject(): T {
    val mapper = jacksonObjectMapper()
    return mapper.readValue(this, T::class.Java)
}

Classname__のTname__を追加で取得する必要はありません。Tname__は、通常のクラスであるかのように使用できます。クライアントの場合、コードは次のようになります。

json.toKotlinObject<MyJsonType>()

重要な注意:Javaでの作業

reifiedname__型のインライン関数はJavaコードから呼び出しできません。

249
s1m0nw1