在Kotlin中推断一个通用类型的地图

考虑一下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 

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

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

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

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

您可以抑制未经检查的强制转换警告 ,而不仅仅是一个语句。

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

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

该解决方案使用带有特定类型参数的内联函数 :函数在其每个调用位置被转换和替换,在编译时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