Inference of a typical Map type in Kotlin

Consider a Java method that describes its type using a Java class as follows:

public <T> T readJson(Class<T> c) throws IOException {

      

This allows you to do something like this:

Map<String, String> map = foo.readJson(Map.class);

      

In java it will warn about an uncontrolled cast, but it will work correctly. However, in Kotlin it will not be so easy, you can try:

foo.readJson(Map::class.java)

      

However, if required Map<String, String>

, this will not work:

Type inference failed. Expected type mismatch.
required Map<String, String>
found Map<*, *>!

      

I also tried to define the StringMap interface:

interface StringMap : Map<String, String>

      

However, this won't work either, it will result in exceptions like this:

Cannot cast ...LinkedTreeMap to ...StringMap

      

What would be the correct way?

+3


source to share


1 answer


Kotlin has nothing to do with Java raw types (which were left in Java for backward compatibility), so the type system does not allow this unchecked assignment to be done implicitly ( star predictions , the closest concept for raw types in Kotlin, are type-safe).

You can do unsupervised selection before Map<String, String>

, thereby expressing what you know about a possible type mismatch at runtime:

@Suppress("UNCHECKED_CAST")
val result = foo.readJson(Map::class.java) as Map<String, String>

      

disable the immediate alert warning for a wider scope than just one statement.

A natural improvement to this solution is to write a utility function to hide the uncontrolled selection in it:

@Suppress("UNCHECKED_CAST")
inline fun <reified T: Any> JsonReader.readJson(): T {
    val result = readJson(T::class.java)
    return result as T
}

      



This solution uses a built-in function with a parameter of type reified : the function is converted and replaced at each of its calling sites, when T

replaced with the specified (or assumed) type at compile time.

Examples of using:

val map = jsonReader.readJson<Map<String, String>>()

      

fun processMap(map: Map<String, String) { /* ... */ }

processMap(jsonReader.readJson()) // Map<String, String> is inferred for this call

      

+5


source







All Articles