Kotlin中如何获取自身为泛型的类的泛型参数类型?
Kotlin泛型嵌套场景下获取泛型参数类型的解决方案
我需要确定某一类型的泛型参数,但该类型本身也带有泛型参数。下面的代码在无泛型嵌套时可以正常运行,但遇到UnitsRatio<Energy, Time>这种泛型嵌套的情况就失效了:
inline fun <reified T : Units> Units?.zero(): Measure<T> { return when (T::class) { Energy::class -> 0.0 * (joules as T) Frequency::class -> 0.0 * (hertz as T) Time::class -> 0.0 * (seconds as T) Spin::class -> 0.0 * (rpm as T) Volume::class -> 0.0 * (liters as T) Temperature::class -> 0.0 * (celsius as T) UnitsRatio<Energy, Time>::class -> 0.0 * (watts as T) // 此处不生效 else -> throw RuntimeException() } }
背景说明
我用Measured库管理物理测量单位,这个zero()函数用来获取值为0的Measure实例,无需显式指定具体单位,由编译器自动推断,目的是实现集合中测量值的求和功能:
val SUnits: Units? = null inline fun <T, reified U : Units> Collection<T>.sumOf(selector: (T) -> Measure<U>): Measure<U> { var sum: Measure<U> = SUnits.zero() for (element in this) { sum += selector(element) } return sum }
这种实现让调用方式很简洁,和现有sumOf变体用法一致,例如:
val totalVolume = bodies.sumOf { it.volume }
问题根源
Kotlin中T::class只能获取原始类型(比如UnitsRatio::class),无法直接拿到泛型参数化后的完整类型(UnitsRatio<Energy, Time>)。这是因为JVM的类型擦除机制会在运行时丢弃泛型参数信息,只有具体化泛型(reified)配合反射才能绕过这个限制。
解决方法
使用Kotlin反射的typeOf<T>()获取包含泛型参数的完整类型信息,然后针对性判断UnitsRatio的具体参数:
首先确保项目引入了kotlin-reflect依赖,之后修改zero()函数如下:
import kotlin.reflect.typeOf import kotlin.reflect.KParameterizedType inline fun <reified T : Units> Units?.zero(): Measure<T> { val type = typeOf<T>() return when { type == typeOf<Energy>() -> 0.0 * (joules as T) type == typeOf<Frequency>() -> 0.0 * (hertz as T) type == typeOf<Time>() -> 0.0 * (seconds as T) type == typeOf<Spin>() -> 0.0 * (rpm as T) type == typeOf<Volume>() -> 0.0 * (liters as T) type == typeOf<Temperature>() -> 0.0 * (celsius as T) type is KParameterizedType && type.rawType == typeOf<UnitsRatio<*, *>>().rawType && type.arguments[0].type == typeOf<Energy>() && type.arguments[1].type == typeOf<Time>() -> 0.0 * (watts as T) else -> throw RuntimeException("不支持的单位类型: $type") } }
优化写法
可以封装一个扩展函数来简化泛型类型判断,让代码更清晰:
inline fun <reified A : Units, reified B : Units> KType.isUnitsRatioOf(): Boolean { return this is KParameterizedType && rawType == typeOf<UnitsRatio<*, *>>().rawType && arguments[0].type == typeOf<A>() && arguments[1].type == typeOf<B>() }
之后在zero()函数中直接调用:
type.isUnitsRatioOf<Energy, Time>() -> 0.0 * (watts as T)
注意事项
- 函数必须保持
inline和reified修饰,否则typeOf<T>()无法在运行时获取具体类型; - 必须添加kotlin-reflect依赖,Gradle项目可在build.gradle中加入:
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
内容的提问来源于stack exchange,提问作者Altline
相关产品推荐
相关产品推荐

