You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)

注意事项

  1. 函数必须保持inline和reified修饰,否则typeOf<T>()无法在运行时获取具体类型;
  2. 必须添加kotlin-reflect依赖,Gradle项目可在build.gradle中加入:
    implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
    

内容的提问来源于stack exchange,提问作者Altline

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 08:40:32