我有签名(ShellInput) -> ShellOutput
的功能。对这些的引用存储在地图中:
mutableMapOf<String, (ShellInput) -> ShellOutput>("trim" to ::trim)
然后从这个地图中获取它们并通过反射调用。现在需要有输入类型可以变化的函数,所以我尝试了:
mutableMapOf<String, (Any) -> ShellOutput>("trim" to ::trim)
但这不起作用。我怎么处理这个?
答案 0 :(得分:3)
事实上,将::trim
作为值放入mutableMapOf<String, (Any) -> ShellOutput>
会破坏类型安全性:当您从地图中取出值后,会将其键入(Any) -> ShellOutput
,让您将Any
作为参数传递给函数。类型系统不允许这样做。
作为一种变通方法,您可以使用star-projected类型Function1<*, ShellOutput>
,这意味着参数类型未知:
mutableMapOf<String, Function1<*, ShellOutput>>("trim" to ::trim)
当您从此地图获取值时,您将看到该函数接受的参数类型为Nothing
。这是完全可以预料的,类型系统再次保留了类型安全性:没有什么可以安全地传递给具有未知参数类型的函数(Nothing
是没有值的类型)。
现在是时候通过使用未经检查的强制转换来向编译器显示您对类型的了解多于它的情况:
@Suppress("UNCHECKED_CAST")
val trim = functions["trim"] as Function1<ShellInput, ShellOutput>
您可以将演员表封装到扩展名中,如下所示:
@Suppress("UNCHECKED_CAST")
fun <T> Map<String, Function1<*, ShellOutput>>.getWithParameter(key: String):
Function1<T, ShellOutput> =
get(key) as Function1<T, ShellOutput>
// Usage:
functions.getWithParameter<ShellInput>("trim")
或者,使用KFunction<ShellOutput>
。它会从未经检查的强制转换中保存你,但它从一开始就不太安全,因为它不控制参数的数量,你可以.call(...)
这样的函数引用任何类型的任意数量的参数。