Infer type from filterIsInstance

Having interface

sealed interface Param<T> {

    val value: T

    @JvmInline
    value class Power(override val value: Float) : Param<Float>

    @JvmInline
    value class Income(override val value: Float) : Param<Float>
}

I put some instances of it to a list

val params = listOf<Param<*>>(
    Param.Power(1f),
    Param.Income(2f)
)

Then I want a function, which should return value of a parameter by it’s type, like

inline fun <reified PARAM : Param<*>> get() = params.filterIsInstance<PARAM>().first().value

But this one does return Any? instead of Float

I can explicitly pass needed type

inline fun <T, reified PARAM : Param<T>> get() = params.filterIsInstance<PARAM>().first().value

In this case use of get() is not clear.

val param = get<Float, Param.Power>()

Is there a way to force compiler infer T from result of params.filterIsInstance<PARAM>().first()?

P.S. The only suitable case of get() I have found is

inline fun <reified T> KClass<out Param<T>>.get(): T = params.filterIsInstance(this.java).first().value

val param = Param.Power::class.get()

But it also far from perfect. Still have no idea how to move KClass<out Param<T>> to reified parameter, and at the same time avoid T.

Is there an issue of just using get<Param.Power>().value?

The reason you aren’t getting Float explicitly is because your Param’s value member is an un-bounded (upper bound of Any?) T, so the compiler can’t be 100% sure that value is a Float, despite us the humans knowing so because Param is sealed and the only current implementations use Float.

As far as I’m aware, you have to either do your workaround, be explicit with the return type, or bound value to Float

This is indeed one option.

An another one could be something like this:

inline fun <reified PARAM : Param<T>, T> get(): T = params.filterIsInstance<PARAM>().first().value

then you can call it this way by skipping the 2nd generic type:

val param = get<Param.Power, _>()
3 Likes

A similar method is already in Kotlin repo:

1 Like