任意型のデータをその実際の型情報を元に処理を行う場合、型情報としてTypeのインスタンスが必要になることがあります。
例えば、Stringクラスを表すType<String>のインスタンスは
String::class.java
とすることでClass<String>を取得することができます。(ClassクラスはTypeインターフェースを実装しています)しかし、ジェネリクス型のタイプパラメータを含めたTypeのインスタンスは、直接取得する手段が提供されていません。
例えば、List<String>のTypeType<List<String>>が必要な場合、
List<String>::class.java
という記述はコンパイルエラーとなります。
このタイプパラメータを含めたType情報はParameterizedTypeとなります。
どうやって取得すれば良いでしょうか?
アブストラクトクラスを実装して取得する
ParameterizedTypeを返すメソッドとして、Classクラスの、getGenericSuperclass()があります。
これはそのクラスの親クラスの情報となりますので、Hoge<T>を継承したFugaから呼び出した場合に、Type<Hoge<T>>を得ることができます。
そして、ParameterizedTypeはgetActualTypeArguments()というメソッドを持っており、そのジェネリクス型の型パラメータのTypeを取得することができ、この型引数もジェネリクス型の場合、ParameterizedTypeが返却されます。
そこで、型引数を持つabstractクラスを作成し、その型引数に取得したいジェネリクス型を指定して継承を行い、getGenericSuperclass()でParameterizedTypeを取得し、getActualTypeArguments()[0]で一つ目の型引数のTypeを取得する。という手順を踏むことで、ジェネリクス型のTypeを取得することができます。
以下のような、abstractクラスを作成し
abstract class TypeToken<T> {
fun getType(): Type =
(javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0]
}
以下のように、インライン実装してgetType()を呼び出すことで取得できます。
object : TypeToken<List<String>>() {}.getType()
Kotlinではインライン関数を使って、以下のように、actualTypeOf()メソッドを作成すると
inline fun <reified T: Any> actualTypeOf(): Type = object : TypeToken<T>() {}.getType()
abstract class TypeToken<T> {
fun getType(): Type =
(javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0]
}
actualTypeOf<List<String>>()
と、よりスマートに取得できます。
KTypeから変換する
Javaの範囲ではややこしい実装が必要ですが、現在のKotlinでは、typeOf()というKTypeを取得するメソッドが用意されています。KTypeからTypeを取得することができるので
@OptIn(ExperimentalStdlibApi::class)
typeOf<List<String>>().javaType
とすることで、Type<List<String>>のインスタンスを取得することができます。javaTypeはまだ実験段階のため、@OptIn(ExperimentalStdlibApi::class)をつけて利用する必要があります。
Experimentalが外れれば、この方法でよさそうです
以上です。