Kotlin 向外投影的函数对象调用()

Kotlin 向外投影的函数对象调用(),kotlin,Kotlin,我是科特林·纽比。我有一组函数,它们接受和解析不同的输入(纯文本、json、xml),但具有相同的输出(和事件实例)。代码如下所示(完整版本位于): 数据类事件(val id:Int) val stringToEvent:(String)->Event={s->Event(s.toInt())} val dummyToEvent:(文档)->Event={{uu->Event(1)} val jsonToEvent:(JsonNode)->Event={j->Event(j.get(“id”).

我是科特林·纽比。我有一组函数,它们接受和解析不同的输入(纯文本、json、xml),但具有相同的输出(和事件实例)。代码如下所示(完整版本位于):

数据类事件(val id:Int) val stringToEvent:(String)->Event={s->Event(s.toInt())} val dummyToEvent:(文档)->Event={{uu->Event(1)} val jsonToEvent:(JsonNode)->Event={j->Event(j.get(“id”).asInt())} fun elementGen(选项:字符串):任意{ //返回字符串、JsonNode或文档 // ... } 趣味主线(args:Array){ val parser=when(args[0]){ “string”->stringToEvent//如果我删除这一行,它将生成 “json”->jsonToEvent “xml”->dummyToEvent else->抛出RuntimeException(“不支持选项”) } 打印(解析器(elementGen(args[0])) } 当我尝试构建时,会出现如下错误:

(44,11):Out投影类型“Function1”禁止使用kotlin.Function1中定义的“公共抽象运算符fun invoke(p1:p1):R”

但是,如果我不使用
stringToEvent
函数,代码似乎可以正确构建和工作


为什么呢?为什么问题似乎只影响
(字符串)->事件
类型函数?

原因是函数的输入类型是
*
(即,它有约束,但约束未知)。无法调用具有星形投影输入类型的函数。从:

对于Foo,其中T是一个逆变类型参数,Foo相当于Foo。这意味着当T未知时,您无法以安全的方式向Foo写入任何内容

提供的参数(
elementGen(args[0])
)的类型是
Any
。这是因为
when
子句中可能的参数类型的并集在
Any
下没有公共类型。因此,
elementGen(args[0])
是函数的无效参数

奇怪的是,尽管kotlinc能够在原始代码中检测到这个错误,但当
String
输入类型被删除时,它却无法检测到这个错误。奇怪的是,在这种情况下编译成功了。假设参数类型(
Any
)不满足输入类型(交叉点类型
Document&JsonNode
,它仍然等同于
*
,就像
Document&JsonNode&String
),我预计这会失败。事实上,如果更改
elementGen
的输出类型,使它们与
解析器的相应函数不匹配,甚至可以看到类型安全性丢失。试试这个,例如:

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])))
}
我想你可能发现了一个bug。如果您选择添加票证,则问题跟踪程序将显示

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)