Hi -
I’m trying to do the following:
inline fun <reified T> getMapper(klass: Class<T>): RFMFunction<T, Event> =
when (klass) {
MClick::class.java -> MClickMapper
else -> { throw RuntimeException("Unknown event type: $klass") }
}
with:
object MClickMapper : RFMFunction<MClick, Event>()
but Kotlin doesn’t recognize that MClickMapper
is a subclass of RFMFunction<MClick, Event>
.
I tried as RFMFunction<MClick, Event>
in the when
, but that didn’t work.
Is there a way to do that?
Thanks!
Here’s a running example that uses an unchecked cast to fix the return type.
class StringToIntMap: Map<String, Int> by mapOf()
fun <T> getMapType(klass: Class<T>): Map<T, Int> {
return when (klass) {
StringToIntMap::class.java -> {
println("Known class $klass")
StringToIntMap() as Map<T, Int> // <-- this unchecked cast.
}
else -> { throw RuntimeException("Unknown event type: $klass") }
}
}
fun main() {
println(getMapType(StringToIntMap::class.java))
println(getMapType(String::class.java))
}
Notice how we do not use reified and therefore don’t inline the function. The when
clause is essentially the same as if (klass == StringToIntMap::class.java)
which won’t work for any subclasses of our generic type StringToIntMap
–I’d mention that in the functions documentation since callers must provide the exact type they want to match.
There are a few things we can do to change this.
For starters, we could use Kotlin’s reflection to call isInstance or isSubclassOf. The runnable demo should fail since you need to include Kotlin’s reflection as a dependency.
open class StringToIntMap: Map<String, Int> by mapOf()
class ChildMap: StringToIntMap()
//sampleStart
inline fun <reified T> getMapType(): Map<T, Int> {
return if (T::class.isInstance(StringToIntMap::class)) {
println("Known class ${T::class}")
StringToIntMap() as Map<T, Int>
}
else throw RuntimeException("Unknown event type: ${T::class}")
}
//sampleEnd
fun main() {
println(getMapType<StringToIntMap>()) // Exact class
println(getMapType<ChildMap>()) // Subclass
println(getMapType<String>()) // Other class
}
^ Note it’s odd to use both reified and take a param of KClass<T>
(or Class<T>
) since they both serve the same purpose of giving you access to the type. Since you have reified, use T::class
instead of taking the redundant param klass
.
You could also use Java’s reflection isAssignableFrom. The main downside is that you can’t use it on non-JVM platforms:
open class StringToIntMap: Map<String, Int> by mapOf()
class ChildMap: StringToIntMap()
inline fun <reified T> getMapType(): Map<T, Int> {
return if (StringToIntMap::class.java.isAssignableFrom(T::class.java)) {
println("Known class ${T::class}")
StringToIntMap() as Map<T, Int>
} else throw RuntimeException("Unknown event type: ${T::class}")
}
fun main() {
println(getMapType<StringToIntMap>()) // Exact class
println(getMapType<ChildMap>()) // Subclass
println(getMapType<String>()) // Other class
}
1 Like
Awesome! Thank you very much!