如何检查 Kotlin 函数内的泛型类型?

Nic*_*Lee 3 arrays json higher-order-functions kotlin

我正在使用 Kotlin 来解析 JSON。例如,我有一个国家的表示:{"code":"US", "name":"United States of America"}Country为了从这样的 a生成一个对象JSONObject,我有这个函数:

val produceCountry = fun (js: JSONObject) =
        Country(js.getString("code"), js.getString("name"))
Run Code Online (Sandbox Code Playgroud)

我可以使用这个函数轻松解析数组Country。然而,除了 数组之外Country,我还有CatCarCartCordlessPhone等数组。每个数组都有自己的produce*函数,将 a 转换JSONObject为该类型的 Kotlin 对象。为了概括数组解析,我有这个函数:

fun <T> produceSetOf(array: JSONArray, element: (JSONObject) -> T): Set<T> {
    val set = mutableSetOf<T>()

    for (i in 0 until array.length())
        set.add(element(array.getJSONObject(i)))

    return set
}
Run Code Online (Sandbox Code Playgroud)

所以我可以调用produceSetOf(jsonArray, produceCountry)遇到一个元素类型为 的数组Country。这也适用于Cat, Car, Cart,数组CordlessPhone

当我看到字符串数组时,问题就出现了。相反array.getJSONObject(i),我必须使用array.getString(i). 实际上,我正在考虑向上面的函数引入另一种参数化类型,并让它以不同的方式进行调用:

fun <S,T> produceSetOf(array: JSONArray, element: (S) -> T): Set<T> {
    val set = mutableSetOf<T>()

    for (i in 0 until array.length()) {
        when (S) {
            is String -> 
                set.add(element(array.getString(i)))
            is JSONObject ->
                set.add(element(array.getJSONObject(i)))
        }
    }

    return set
}
Run Code Online (Sandbox Code Playgroud)

当然,Kotlin 不允许我这样做。有什么建议我可以如何做到这一点,同时保持通用性produceSetOf()并且不引入另一层抽象(例如元素迭代器或将索引转换为 String/JSONObject 的函数)?

谢谢。

And*_*ann 6

这是使用具体化类型参数的一种可能的解决方案。

inline fun <reified S, T> produceSetOf(array: JsonArray, element: (S) -> T): Set<T> {
    val set = mutableSetOf<T>()

    for (i in 0 until array.size()) {
        when (S::class) {
            String::class -> set.add(element(array[i].string as S))
            JsonObject::class -> set.add(element(array[i].obj as S))
        }
    }

    return set
}

val stringArray = listOf("1", "2").toJsonArray()

val stringSet = produceSetOf<String, Int>(stringArray) { it.toInt() }
println(stringSet) // prints [1, 2]

val objArray = listOf(jsonObject("key" to "value"), jsonObject("key" to "other")).toJsonArray()
val objSet = produceSetOf<JsonObject, String>(objArray) { it["key"].string }
println(objSet) // print [value, other]
Run Code Online (Sandbox Code Playgroud)

我使用 gson 作为 Json 对象,因为我不知道你的来自哪里。

一个可能的更短的解决方案:

inline fun <reified S, T> produceSetOf(array: JsonArray, element: (S) -> T): Set<T> = array.map {
    when (S::class) {
        String::class -> element(it.string as S)
        JsonObject::class -> element(it.obj as S)
        else -> throw UnsupportedOperationException("${S::class.simpleName} is not supported")
    }
}.toSet()
Run Code Online (Sandbox Code Playgroud)