Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

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

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

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

inline fun <reified T : Any> List<*>.checkItemsAre(): List<T> {
    return this.filterIsInstance<T>().takeIf { it.size == this.size }
        ?: throw IllegalArgumentException("Can't convert raw List to type safe List")
}

But I can’t fund analog for map.

How can I fix it ?

>Solution :

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>()
        )
    }
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading