How can I check for generic type in Kotlin
Generic ProgrammingKotlinGeneric Programming Problem Overview
I'm trying to test for a generic type in Kotlin.
if (value is Map<String, Any>) { ... }
But the compiler complains with
> Cannot check for instance of erased type: jet.Map
The check with a normal type works well.
if (value is String) { ... }
Kotlin 0.4.68 is used.
What am I missing here?
Generic Programming Solutions
Solution 1 - Generic Programming
The problem is that type arguments are erased, so you can't check against the full type Map
To work around this, use wildcards:
if (value is Map<*, *>) {...}
Solution 2 - Generic Programming
I think this is more appropriate way
inline fun <reified T> tryCast(instance: Any?, block: T.() -> Unit) {
if (instance is T) {
block(instance)
}
}
Usage
// myVar is nullable
tryCast<MyType>(myVar) {
// todo with this e.g.
this.canDoSomething()
}
Another shorter approach
inline fun <reified T> Any?.tryCast(block: T.() -> Unit) {
if (this is T) {
block()
}
}
Usage
// myVar is nullable
myVar.tryCast<MyType> {
// todo with this e.g.
this.canDoSomething()
}
Solution 3 - Generic Programming
JVM removes the generic type information. But Kotlin has reified generics. If you have a generic type T, you can mark type parameter T of an inline function as reified so it will be able to check it at runtime.
So you can do:
inline fun <reified T> checkType(obj: Object, contract: T) {
if (obj is T) {
// object implements the contract type T
}
}
Solution 4 - Generic Programming
I'm gonna give a workaround solution but I think its clean, kind of
try{
(value as Map<String,Any>?)?.let { castedValue ->
doYourStuffHere() //using castedValue
}
}catch(e: Exception){
valueIsNotOfType() //Map<String,Any>
}
Solution 5 - Generic Programming
I have tried the solution above with tryCast<Array<String?>>
and, I guess, in my specific task in listing with many castings involved it was no so great idea, because it was slowing the performance drastically.
This is the solution I did finally - manually check the entries and call methods, like this:
fun foo() {
val map: Map<String?, Any?> = mapOf()
map.forEach { entry ->
when (entry.value) {
is String -> {
doSomeWork(entry.key, entry.value as String)
}
is Array<*> -> {
doSomeWork(entry.key, (entry.value as? Array<*>)?.map {
if (it is String) {
it
} else null
}?.toList())
}
}
}
}
private fun doSomeWork(key: String?, value: String) {
}
private fun doSomeWork(key: String?, values: List<String?>?) {
}