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


144

reifiedキーワードの目的を理解しようとしています。どうやら、ジェネリックスについての考察を可能にしているようです

ただし、省略した場合も同様に機能します。これが実際の違いを生むときは誰でも説明してくれますか


リフレクションとは何か知っていますか?また、型消去について聞いたことがありますか?
Mibac

3
ジェネリック型パラメーターは実行時に消去されます。まだ消去していない場合は、型消去についてお読みください。インライン関数の型パラメーターがメソッド本体をインライン化するだけでなく、T :: class.java(通常のジェネリック型では実行できない)などの操作を可能にするジェネリック型パラメーターも具体化しました。私は今、完全な答えを肉付けする時間がないので...コメントとして置く
F.ジョージ

これにより、リフレクションに依存したり、型を引数として渡したりすることなく、関数の具体的なジェネリック型にアクセスできます。
BladeCoder 2017

回答:


368

TL; DR:何がreified良いのか

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

以下のような一般的な関数の本体ではmyGenericFun、あなたはタイプにアクセスすることはできませんT、それはですので、コンパイル時にのみ使用できますが、消去実行時に。したがって、ジェネリック型を関数本体の通常のクラスとして使用する場合は、に示すように、クラスをパラメーターとして明示的に渡す必要がありますmyGenericFun

ただし、具体化しinline関数を作成すると、実行時にものタイプにアクセスできるため、追加で渡す必要はありません。あなたはで作業することができますが、変数があるかどうかを確認したい場合があります例えば、それは通常のクラスであるかのようにインスタンスあなたが簡単に行うことができ、その後、: 。 TTClass<T>T TmyVar is T

タイプをinline持つこのような関数は次のようになります。reifiedT

inline fun <reified T> myGenericFun()

どのようreifiedな作品

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


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

a)タイプを具体化しない最初のアプローチ

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

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

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

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

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

data class MyJsonType(val name: String)

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

c)Kotlinの方法: reified

型パラメーターを持つinline関数を使用すると、関数を別の方法で実装できreifiedますT

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

追加で取得する必要ClassTなく、T通常のクラスのように使用できます。クライアントの場合、コードは次のようになります。

json.toKotlinObject<MyJsonType>()

重要な注意:Javaの操作

reifiedタイプを含むインライン関数は、Javaコードから呼び出すことができません


6
総合的な回答ありがとうございます!それは実際に理にかなっています。とにかく、関数がインライン化されている場合、なぜ具体化が必要なのでしょうか。それは型消去を残し、とにかく関数をインライン化しますか?これは私にとって一種の無駄のようです。関数をインライン化する場合、使用されている型もインライン化する可能性がありますか、またはここで何か間違っていると思いますか?
hl3mukkel 2017

6
あなたのフィードバックをありがとう、実際に私はあなたに答えを与えるかもしれないことを言及するのを忘れています:通常のインライン関数はJavaから呼び出すことができますが、型パラメータを具体化したものはできません!これが、インライン関数のすべての型パラメーターが自動的に具体化されるとは限らない理由だと思います。
s1m0nw1 2017

関数が具体化されたパラメーターと具体化されていないパラメーターが混在している場合はどうなりますか?それはとにかくJavaから呼び出される資格がないようにします、なぜすべての型パラメーターを自動的に具体化しないのですか?kotlinがすべての型パラメーターに対して明示的に具体化を指定する必要があるのはなぜですか?
Vairavan 2018

1
スタックの上位の呼び出し元がjson.toKotlinObject <MyJsonType>()を必要とせず、異なるオブジェクトに対してjson.toKotlinObject <T>()を必要とする場合はどうなりますか?
7

1

シンプル

* reifiedは、コンパイル時に使用する許可を与えることです(de関数内のTにアクセスするため)

例えば:

 inline fun <reified T:Any>  String.convertToObject(): T{

    val gson = Gson()

    return gson.fromJson(this,T::class.java)

}

のような使用:

val jsonStringResponse = "{"name":"bruno" , "age":"14" , "world":"mars"}"
val userObject = jsonStringResponse.convertToObject<User>()
  println(user.name)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.