Altline
Altline

Reputation: 383

How can I determine the types of generic parameters of a class that is itself generic?

I need to determine the type of a generic parameter in a situation where the type in question can have its own generic types. Below is code that worked until this nesting of generics came up.

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) // this does not work
        else -> throw RuntimeException()
    }
}

Is there a way to get this generic type information about UnitsRatio?

Background

I'm using Measured, a library to help with managing physical measurement units. Basically, my function is supposed to be a way to obtain a measure with a value of 0 without knowing the exact measurement unit explicitly, as it should be inferred by the compiler. This would enable me to get a sum of measurement values from a collection.

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
}

This makes the usage clean and similar to existing sumOf variants. For example:

val totalVolume = bodies.sumOf { it.volume }

Upvotes: 1

Views: 53

Answers (1)

Sweeper
Sweeper

Reputation: 274480

One way would be to compare against the types, rather than the classes of T and the unit types.

inline fun <reified T : Units> Units?.zero(): Measure<T> {
    return when (typeOf<T>()) {
        typeOf<Energy>() -> 0.0 * (joules as T)
        typeOf<Frequency>() -> 0.0 * (hertz as T)
        typeOf<Time>() -> 0.0 * (seconds as T)
        typeOf<Spin>() -> 0.0 * (rpm as T)
        typeOf<Volume>() -> 0.0 * (liters as T)
        typeOf<Temperature>() -> 0.0 * (celsius as T)
        typeOf<UnitsRatio<Energy, Time>>() -> 0.0 * (watts as T)
        else -> throw RuntimeException()
    }
}

Note that this requires kotlin.reflect. There might be a better solution specific to the Measured library that you are using, which I am not familiar with.

Upvotes: 1

Related Questions