【问题标题】:In kotlin, how to return an instance defined by generic class parameter在kotlin中,如何返回由泛型类参数定义的实例
【发布时间】:2016-07-11 05:36:47
【问题描述】:

我正在尝试为针对 kotlin 1.0.3 的 Web 框架编写一个不错的 Kotlin 包装器。在那我试图将一个函数混合到请求中,让它通过使用杰克逊的 JSON 转换返回一个 bean。

所以在我的图书馆里,我有以下内容

private val mapper: ObjectMapper = ObjectMapper().registerModule(KotlinModule())
fun <T : Any> Request.asDataBean(type: KClass<T>): T = mapper.readValue(this.body(), type.java)

但是当我去使用这样的代码时

post("/hello", { req, res ->
    val bean = req.asDataBean(TestBean::class)
})

错误地说 bean 的期望值为 Any。我想要的是让我的 API 像上面那样工作,其中传递给 asDataBean 方法的通用“类”定义是返回的值的类型。

我也试过了

fun <T> Request.asDataBean(type: KClass<*>): T = mapper.readValue(this.body(), type.java) as T

以及将使用代码更改为

val bean: TestBean = req.asDataBean(TestBean::class)

希望让它工作,但他们在使用代码时也会给出完全相同的错误。

如何让它使用由作为参数传入的类类型定义的泛型(非常类似于所有 spring api 在 java 中的工作方式)?

【问题讨论】:

  • 很奇怪,第一个sn-p对我有用。您能否提供一个没有外部依赖的最小独立示例?
  • 我不确定我是否理解。我创建了一个 github 存储库以在 github.com/sepatel/kotlin-seed 处显示代码。这是整个项目,希望能帮助更好地描述问题?它给出的错误是使用 API 的编译错误,说“e: Main.kt: (23, 9): Expected a value of type Any e: Main.kt: (23, 34): Type inference failed:没有足够的信息来推断 fun Request.asData(type: KClass): T" 中的参数 T

标签: generics kotlin kotlin-extension


【解决方案1】:

您示例中的post 函数需要route: (Request, Response) -&gt; Any 参数,这是一个函数,接受请求和响应并返回一些非空值。

当您将 lambda 表达式用作 route 时,它的返回类型是从 lambda 主体的最后一个表达式推断出来的,并且由于在 Kotlin 中赋值不是表达式,因此以下 lambda 没有返回类型全部:

{ req, res ->
    val bean = req.asDataBean(TestBean::class)
}

要使其正常工作,只需将bean 设为最后一个表达式

{ req, res ->
    val bean = req.asDataBean(TestBean::class)
    bean
}

或者根本不使用赋值:

{ req, res -> req.asDataBean(TestBean::class) }

注意:我使用了asDataBean函数的如下定义:

fun <T: Any> Request.asDataBean(type: KClass<T>): T =
    mapper.readValue(this.body(), type.java)

你也可以做一个具体化的重载,它调用非具体化的重载,这样你就不必暴露所有的内部:

inline fun <reified T: Any> Request.asDataBean(): T = 
    asDataBean(T::class)

req.asDataBean<TestBean>() // usage

【讨论】:

  • 谢谢。我不敢相信我误解了这样的错误信息。读完你的理由后,我觉得自己像个新手。感谢关于 reified 的提示,我从来没有想过这个问题,这听起来是解决手头问题的最佳方法。
【解决方案2】:

更符合 Kotlin 习惯的方式是使用 reified type parameters

inline fun <reified T : Any> Request.asDataBean(): T = mapper.readValue(this.body(), T::class.java)

然后可以这样消费:

post("/hello", { req, res ->
    val bean = req.bodyAs<TestBean>()
    res.body("Ok")
})

【讨论】:

  • 为了内联,您必须公开所有变量,在这种情况下,映射器是在库中私有定义的,因此内联不起作用。另外,我已经尝试按照您提到的方式通过将映射器公开并使其具体化,但它仍然不是可消耗的,正如您所描述的那样给出预期的任何类型的编译器错误。
  • 创建了一个分支github.com/sepatel/kotlin-seed/tree/reified 以证明即使经过具体化,它也无法在使用时给出“预期值为任何类型”的错误。
  • @Ryba post("/hello", {res,req-&gt; ... 兰巴参数必须返回Any。 Kotlin 中的赋值不是表达式,因此它没有价值。您需要指定返回值。
猜你喜欢
  • 2016-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-21
  • 2016-07-15
相关资源
最近更新 更多