How to safety cast raw Map to Map<String, List<Int>> in Kotlin?

37 Views Asked by At

I need a function which will cast Map to Map<String, List<Int>>

Currently I use unsafe "as" but I receive a fair warning about it. I want to fix it.

For list part I was able to implement function

inline fun <reified T : Any> List<*>.checkItemsAre(): List<T> {
    return this.filterIsInstance<T>().takeIf { it.size == this.size }
        ?: throw IllegalArgumentException("blablabla")
}

But I can't fund analog for map.

How can I fix it ?

1

There are 1 best solutions below

0
Sweeper On BEST ANSWER

If you are looking for something similar to the checkItemsAre for Lists that you showed, you can write such a method for Maps.

inline fun <reified K : Any, reified V: Any> Map<*, *>.checkKeyValuesAre() =
    entries.associate {
        Pair(
            (it.key as? K) ?: throw IllegalArgumentException(),
            (it.value as? V) ?: throw IllegalArgumentException()
        )
    }

Just like checkItemsAre, this creates a new Map if all the keys are of type K and all the values are of type V.

However, this wouldn't completely check nested generics like Map<String, List<Int>>. If you want to handle that, you would have to add special cases for List, e.g.

return if (typeOf<V>().classifier == List::class) {
    val listElemType = typeOf<V>().arguments[0].type?.classifier as KClass<*>
    entries.associate {
        Pair(
            (it.key as? K) ?: throw IllegalArgumentException(),
            (it.value as List<*>).checkItemsAre(listElemType)
        )
    }
} else {
    // the code in the first code snippet...
}
fun <T : Any> List<*>.checkItemsAre(clazz: KClass<T>): List<T> {
    return this.filterIsInstance(clazz.java).takeIf { it.size == this.size }
        ?: throw IllegalArgumentException()
}

If you are only going to handle maps with values of type List, I would just assume the map's values are lists:

inline fun <reified K : Any, reified E: Any> Map<*, *>. checkKeyAndListValuesAre() =
    entries.associate {
        Pair(
            (it.key as? K) ?: throw IllegalArgumentException(),
            (it.value as List<*>).checkItemsAre<E>()
        )
    }