我是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)用作函数类型参数?由于TestMessage1
和TestMessage2
都实现了此接口(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
。这使得这种抽象开始时不是很有用。