我最近才开始尝试 Kotlin,到目前为止我对它感到非常惊讶。但是,我不能完全理解泛型在这里是如何工作的。
我想读取如下所示的 YAML 文件:
- id: acrobatics
name: Akrobatik
description:
...
一个简单的数据类将其映射到:
data class Skill (
val id: String,
val name: String,
val description: String
)
现在,进入有趣的部分。 显然,它确实适用于泛型!
我从这个函数开始,它按预期完成了它的工作:
fun loadSkills(): List<Skill> {
val resource: URL = classLoader.getResource("rulebook/skills.yml")!!
val items: List<Skill> = resource.openStream()
.bufferedReader().use() { reader ->
objectMapper.readValue<List<Skill>>(reader)
}
return items
}
但是,我想重用它,所以我尝试创建一个通用函数:
protected fun <R: Any> loadList(path: String): List<R> {
val resource: URL = classLoader.getResource(path)!!
val items: List<R> = resource.openStream()
.bufferedReader().use() { reader ->
objectMapper.readValue<List<R>>(reader)
}
return items
}
我原来的函数只是调用:
return loadList<Skill>("rulebook/skills.yml")
现在我的测试失败了,因为我得到了一个 LinkedHashMaps 列表,这似乎是 YAML 解析器用来映射对象的默认数据类型。
我阅读了更多内容并尝试将我的方法签名更改为具有具体类型参数的内联函数:
protected inline fun <reified R: Any> loadList(path: String): List<R>
但这似乎并没有改变任何东西。
有什么办法可以优雅地完成这项工作吗?
最佳答案
由于在运行时不存在所有泛型类型信息,因此您需要以某种方式将其提供给 Jackson。 Reified type 保留该信息,但 AFAIK Jackson 不使用它,因此您必须手动执行。例如,像这样:
inline fun <reified T> loadSkills(path: String): List<T> {
val resource: URL = javaClass.classLoader.getResource(path)
val type = objectMapper.typeFactory.constructParametricType(List::class.java, T::class.java)
val items: List<T> = resource.openStream()
.bufferedReader().use { reader ->
objectMapper.readValue(reader, type)
}
return items
}
编辑 : 事实证明,jackson-kotlin-module 对嵌套泛型类型的自动计算存在限制。
如果您尝试为单个技能(没有列表)解析 yaml(或其他任何内容),即使在泛型函数中它也可以正常工作,但是一旦您想要像 List< Skill> 那样进行嵌套,它就无法正确识别类型并产生 List of哈希映射
关于generics - 使用通用函数不适用于使用 jackson 加载 YAML 列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56752508/