Consider a Java method which infers its type by Java class as follows:
public <T> T readJson(Class<T> c) throws IOException {
This allows doing something like this:
Map<String, String> map = foo.readJson(Map.class);
In java it will warn about unchecked cast, but it will work correctly. However in Kotlin, this will not be so easy, one could try using:
foo.readJson(Map::class.java)
However if Map<String, String>
will be required, it will not work:
Type inference failed. Expected type mismatch.
required Map<String, String>
found Map<*, *>!
I also tried defining an interface StringMap:
interface StringMap : Map<String, String>
However that does not work either, it will lead to exceptions like this:
Cannot cast ...LinkedTreeMap to ...StringMap
What would be a correct way of doing this?
There are no direct ways to do this in Kotlin. In order to check the generic type, we need to create an instance of the generic class<T> and then we can compare the same with our class.
Kotlin map is a collection that contains pairs of objects. Map holds the data in the form of pairs which consists of a key and a value. Map keys are unique and the map holds only one value for each key. Kotlin distinguishes between immutable and mutable maps.
When we define a collection with "*", it should contain the object of only that type. There should not be any mix and match between the data types inside a collection. If we use "Any", we can mix and match the data types, which means we can have multiple data types in a collection.
Kotlin does not have anything like Java raw types (which were left in Java for backward compatibility), and the type system therefore does not allow this kind of unchecked assignment to be made implicitly (star projections, the closest concept to raw types in Kotlin, retain type safety).
You can make an unchecked cast to Map<String, String>
, thus expressing that you are aware of a possible type mismatch at runtime:
@Suppress("UNCHECKED_CAST")
val result = foo.readJson(Map::class.java) as Map<String, String>
You can suppress the unchecked cast warning for a broader scope than just one statement.
A natural improvement of this solution is writing a util function to hide the unchecked cast 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 an inline function with a reified type parameter: the function is transformed and substituted at each of its call sites, with T
replaced by the specified (or inferred) type at compile time .
Usage examples:
val map = jsonReader.readJson<Map<String, String>>()
fun processMap(map: Map<String, String) { /* ... */ }
processMap(jsonReader.readJson()) // Map<String, String> is inferred for this call
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With