在out -jected Function对象上调用()

时间:2018-02-15 17:07:46

标签: kotlin

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类型函数?

1 个答案:

答案 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