如何在Kotlin函数中检查泛型?

时间:2018-07-26 09:11:24

标签: arrays json kotlin higher-order-functions

我正在使用Kotlin解析JSON。例如,我具有以下国家的代表形式:{"code":"US", "name":"United States of America"}。为了从这样的Country生成JSONObject对象,我具有以下功能:

val produceCountry = fun (js: JSONObject) =
        Country(js.getString("code"), js.getString("name"))

我可以使用此函数轻松解析Country数组。除了Country数组之外,我还有CatCarCartCordlessPhone等数组。每个数组都有自己的{{1} }函数将produce*转换为该类型的Kotlin对象。为了概括数组分析,我具有以下功能:

JSONObject

因此,我可以在遇到元素类型为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 } 类型的数组时调用produceSetOf(jsonArray, produceCountry)。这对CountryCatCarCart的数组也很有效。

当我看到一个字符串数组时出现问题。我必须使用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 } 通用性的同时又不引入另一层抽象的情况下(例如元素迭代器或将索引转换为String / JSONObject的函数)如何实现?

谢谢。

1 个答案:

答案 0 :(得分:1)

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

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]

我将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()