【问题标题】:Inferring a generic type of Map in Kotlin在 Kotlin 中推断通用类型的 Map
【发布时间】:2017-05-16 11:37:11
【问题描述】:

考虑一个通过 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&lt;String, String&gt;,它将不起作用:

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

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

interface StringMap : Map<String, String>

但是这也不起作用,它会导致这样的异常:

Cannot cast ...LinkedTreeMap to ...StringMap

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

【问题讨论】:

  • 请注意,您在此处通过使用 Class 而不是 Type 来使用已擦除类型,大多数 JSON 解析器允许您指定 Type、TypeRef、TypeReference 或类似的而不是 Class,如果出现以下情况,您应该考虑进行更改你的库支持它,否则泛型参数将被忽略。

标签: kotlin


【解决方案1】:

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

您可以对Map&lt;String, String&gt; 进行未经检查的强制转换,从而表示您知道在运行时可能存在类型不匹配:

@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

【讨论】:

    猜你喜欢
    • 2017-08-01
    • 2018-08-21
    • 2020-12-30
    • 1970-01-01
    • 1970-01-01
    • 2018-05-15
    • 1970-01-01
    • 2018-10-08
    • 1970-01-01
    相关资源
    最近更新 更多