1. ホーム
  2. generics

[解決済み] Kotlinでジェネリックパラメータクラスを取得する方法

2023-01-22 12:08:48

質問

Firebaseの snapshot.getValue() は以下のように呼ばれることを期待します。

snapshot?.getValue(Person::class.java)

しかし、私は Person をクラス宣言で渡される一般的なパラメータで置き換えたいと思います。

class DataQuery<T : IModel> 

で、そのジェネリックパラメータを使って、以下のようなことをします。

snapshot?.getValue(T::class.java)

というエラーが表示されます。

はクラスリテラルの左側で使用することができます。

C#のようにジェネリックパラメータにクラス制約を与えることは可能ですか?またはジェネリックパラメータの型情報を取得するために使用できる他の構文がありますか?

どのように解決するのですか?

ジェネリックパラメータTを持つクラスでは、JVMが型情報を消去するため、Tの型情報がないため、これを行うことはできません。 したがって、このようなコードは動作しません。

class Storage<T: Any> {
    val snapshot: Snapshot? = ...

    fun retrieveSomething(): T? {
        return snapshot?.getValue(T::class.java) // ERROR "only classes can be used..."
    }
}

しかし、Tの型を再定義し、インライン関数内で使用すれば、これを動作させることができます。

class Storage {
    val snapshot: Snapshot? = ...

    inline fun <reified T: Any> retrieveSomething(): T? {
        return snapshot?.getValue(T::class.java)
    }
}

インライン関数が public の場合、クラスの public メンバにのみアクセスできることに注意してください。 1つはインラインではないクラスパラメータを受け取り、プライベートな内部にアクセスするもので、もう1つは推論された型パラメータから再定義を行うインラインヘルパー関数です。

class Storage {
    private val snapshot: Snapshot? = ...

    fun <T: Any> retrieveSomething(ofClass: Class<T>): T? {
        return snapshot?.getValue(ofClass)
    }

    inline fun <reified T: Any> retrieveSomething(): T? {
        return retrieveSomething(T::class.java)
    }
}

また KClass の代わりに Class を使うことで、Kotlin のみの呼び出し元は単に MyClass::class の代わりに MyClass::class.java

クラスがジェネリックのインラインメソッドと協調するようにしたい場合(つまり、クラスが Storage 型のオブジェクトのみを格納する T ):

class Storage <T: Any> {
    val snapshot: Snapshot? = ...

    inline fun <reified R: T> retrieveSomething(): R? {
        return snapshot?.getValue(R::class.java)
    }
}

インライン関数で再定義された型へのリンクです。 https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters