Kotlin newby在这里。我有一组接受和解析不同输入(纯文本,json,xml)但具有相同输出(和Event实例)的函数。代码如下所示(https://pastebin.com/UNJFGZsm处的完整版本):
data class Event(val id: Int) val stringToEvent: (String) -> Event = { s -> Event(s.toInt()) } val dummyToEvent: (Document) -> Event = { _ -> Event(1) } val jsonToEvent: (JsonNode) -> Event = { j -> Event(j.get("id").asInt()) } fun elementGen(opt: String): Any { // return a String, or a JsonNode, or a Document // ... } fun main(args : Array) { val parser = when (args[0]) { "string" -> stringToEvent // it builds if I remove this line "json" -> jsonToEvent "xml" -> dummyToEvent else -> throw RuntimeException("Option not supported") } print(parser(elementGen(args[0]))) }
当我尝试构建时,我得到以下错误:
(44,11):Out-projection类型'Function1< *,Event>'禁止使用'public abstract operator fun invoke(p1:P1):在kotlin.Function1中定义的R
但是,如果我不使用stringToEvent
函数,代码似乎可以正常构建并正常工作。
为什么?为什么问题似乎只影响(String) -> Event
类型函数?
答案 0 :(得分:1)
原因是函数的输入类型是*
(即,它具有约束但约束未知)。无法调用具有星形投影输入类型的功能。来自Kotlin generics page:
对于Foo< in T>,其中T是逆变型参数,Foo< *>相当于Foo< in Nothing>。这意味着没有什么可以写给Foo< *>当T未知时,以安全的方式。
提供的参数(elementGen(args[0])
)的类型为Any
。这是因为when
子句中可能的参数类型的并集在Any
下没有共同类型。因此,elementGen(args[0])
是函数的无效参数。
看起来奇怪的是,虽然kotlinc能够在原始代码中检测到此错误,但在删除String
输入类型时无法检测到它。在这种情况下编译成功是很奇怪的。鉴于参数类型(Any
)不满足输入类型(交集类型Document & JsonNode
,它仍然等同于*
,就像Document & JsonNode & String
),我会期望这会失败。实际上,如果更改elementGen
的输出类型,使得它们与parser
的相应函数不匹配,您甚至可以看到类型安全性丢失。试试这个,例如:
fun elementGen(opt: String): Any {
return when (opt) {
"string" -> "1"
"json" -> {
"1"
// val mapper = ObjectMapper()
// mapper.readTree("{\"id\": 1 }")
}
"xml" -> {
DocumentBuilderFactory.newInstance().newDocumentBuilder().parse("<id>1</id>")
}
else -> throw RuntimeException("Option not supported")
}
}
fun main(args: Array<String>) {
val parser = when (args[0]) {
// "string" -> stringToEvent
"json" -> jsonToEvent
"xml" -> dummyToEvent
else -> throw RuntimeException("Option not supported")
}
print(parser(elementGen(args[0])))
}
您可以看到编译仍然成功,但在运行时引发了类强制转换异常:
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to com.fasterxml.jackson.databind.JsonNode
at com.example.demo.config.TestKt$jsonToEvent$1.invoke(test.kt)
at com.example.demo.config.TestKt.main(test.kt:39)
我想你可能发现了一个错误。如果您选择添加票证,则问题跟踪器为here。