如何检查 Kotlin 函数中的泛型类型?
How to check a generic type inside a Kotlin function?
我正在使用 Kotlin 来解析 JSON。例如,我有一个国家的表示:{"code":"US", "name":"United States of America"}
。要从这样的 JSONObject
生成一个 Country
对象,我有这个函数:
val produceCountry = fun (js: JSONObject) =
Country(js.getString("code"), js.getString("name"))
我可以使用这个函数轻松解析 Country
的数组。但是,除了 Country
数组外,我还有 Cat
、Car
、Cart
、CordlessPhone
等数组。每个数组都有自己的 produce*
函数将 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
}
所以我可以在遇到元素类型为 Country
的数组时调用 produceSetOf(jsonArray, produceCountry)
。这也适用于 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
}
当然,Kotlin 不允许我这样做。任何建议我如何在保持 produceSetOf()
的通用性并且不引入另一层抽象(例如元素迭代器或将索引转换为 String/JSONObject 的函数)的情况下做到这一点?
谢谢。
这是一种使用具体化类型参数的可能解决方案。
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]
我为 Json 对象使用了 gson,因为我不知道你的对象来自哪里。
可能更短的解决方案:
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()
我正在使用 Kotlin 来解析 JSON。例如,我有一个国家的表示:{"code":"US", "name":"United States of America"}
。要从这样的 JSONObject
生成一个 Country
对象,我有这个函数:
val produceCountry = fun (js: JSONObject) =
Country(js.getString("code"), js.getString("name"))
我可以使用这个函数轻松解析 Country
的数组。但是,除了 Country
数组外,我还有 Cat
、Car
、Cart
、CordlessPhone
等数组。每个数组都有自己的 produce*
函数将 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
}
所以我可以在遇到元素类型为 Country
的数组时调用 produceSetOf(jsonArray, produceCountry)
。这也适用于 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
}
当然,Kotlin 不允许我这样做。任何建议我如何在保持 produceSetOf()
的通用性并且不引入另一层抽象(例如元素迭代器或将索引转换为 String/JSONObject 的函数)的情况下做到这一点?
谢谢。
这是一种使用具体化类型参数的可能解决方案。
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]
我为 Json 对象使用了 gson,因为我不知道你的对象来自哪里。
可能更短的解决方案:
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()