我是Kotlin的新手,我想要实现的目标是在动态类型化语言(如Python和JavaScript)中非常简单,但是在Kotlin中则不那么容易。我有一组消息处理程序函数,它们接受消息对象。每个消息类都实现Message接口(interface)。我想将每个消息处理程序函数映射到String键:

interface Message

data class TestMessage1(val body: String): Message
data class TestMessage2(val body: String): Message

fun testMessage1Handler(msg: TestMessage1) { println(msg.body) }
fun testMessage2Handler(msg: TestMessage2) { println(msg.body) }

val functions = mapOf<String, (Message)->Unit> (
        "testMessage1" to ::testMessage1Handler,
        "testMessage2" to ::testMessage2Handler
)

这段代码给了我两个错误:
error: type inference failed.
Expected type mismatch: inferred type is
Pair<String, KFunction1<@ParameterName Line_1.TestMessage1, Unit>>
but
Pair<String, (Line_1.Message) -> Unit> was expected

error: type inference failed.
Expected type mismatch: inferred type is
Pair<String, KFunction1<@ParameterName Line_1.TestMessage2, Unit>>
but
Pair<String, (Line_1.Message) -> Unit> was expected

为什么不能将Message接口(interface)用作函数类型参数?由于TestMessage1TestMessage2都实现了此接口(interface),因此对我来说似乎是正确的。您将如何实现这样的目标?

有一个相关的问题How to save a function reference as the value in a Map type, and invoke it with a parameter later on in Kotlin?,但我不想将消息处理程序参数msg类型更改为Any

最佳答案

让我们以另一种方式来看。

如果不是通过尝试但没有指定类型,而是通过指定不正确的类型,让Kotlin有机会进行推断,该怎么办:

val functions: String = mapOf(
    "testMessage1" to ::testMessage1Handler,
    "testMessage2" to ::testMessage2Handler
)

这将产生:
inferred type is Map<String, KFunction1<*, Unit>> but String was expected.

现在,如果您放置了该签名,它将实际编译:
val functions = mapOf<String, KFunction1<*, Unit>>(
    "testMessage1" to ::testMessage1Handler,
    "testMessage2" to ::testMessage2Handler
)

这类似于:
val functions: Map<String, KFunction1<*, Unit>> = mapOf(
    "testMessage1" to ::testMessage1Handler,
    "testMessage2" to ::testMessage2Handler
)

但是,当尝试调用它时,您会得到提示,说明这是不正确的:
functions["testMessage1"]?.invoke(TestMessage1("a"))

得到你
Type mismatch.
Required: Nothing
Found: TestMessage1

请注意,Kotlin推断您的输入类型为Nothing
为了理解原因,让我们只看一个函数:
fun testMessage1Handler(msg: TestMessage1)

并问问自己:除了TestMessage1之外,此函数还可以接收其他什么类型?没有其他类型。它无法接收TestMessage2。它无法接收Message,因为Message不一定是TestMessage1

对于第二个功能,我们将得到相同的答案。因此,两者的通用类型是Nothing。这使得这种抽象开始时不是很有用。

07-24 09:38
查看更多