在Kotlin中推断一般类型的Map

时间:2017-05-15 14:55:02

标签: kotlin

考虑一个Java方法,它通过Java类推断出它的类型如下:

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

这允许做这样的事情:

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

在java中,它会警告未经检查的强制转换,但它会正常工作。但是在Kotlin中,这不会那么容易,可以尝试使用:

foo.readJson(Map::class.java)

但是,如果需要Map<String, String>,则无效:

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

我也尝试过定义一个接口StringMap:

interface StringMap : Map<String, String>

然而,这也不起作用,它会导致像这样的例外:

Cannot cast ...LinkedTreeMap to ...StringMap

这样做的正确方法是什么?

1 个答案:

答案 0 :(得分:9)

Kotlin没有Java原始类型(为了向后兼容而留在Java中),因此类型系统不允许隐式地进行这种未经检查的赋值(star projections,最接近的概念在Kotlin的原始类型,保持类型安全)。

您可以对Map<String, String>进行未经检查的强制转换,从而表示您在运行时意识到可能的类型不匹配:

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

您可以suppress the unchecked cast warning获得更广泛的范围而不仅仅是一个陈述。

此解决方案的一个自然改进是编写一个util函数来隐藏未经检查的强制转换:

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

此解决方案使用inline function with a reified type parameter:函数在每个调用站点进行转换和替换,在编译时将T替换为指定(或推断)类型。

用法示例:

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

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

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