Kotlin 高阶函数签名类型:匹配函数对象和类型擦除
Kotlin High order function signature types: matching function objects and type erasure
看来我在这里遗漏了一些非常重要的东西。 Kotlin 函数类型使用称为 FunctionN 的特定 classes 实现,并使用泛型定义接收器、参数和 return类型。存储函数引用时是否有语法可以保留函数签名?或者任何其他方式来执行我在下面的代码中所做的事情?
那是因为这一切都是乐趣和游戏,直到您希望 实际 存储 他们输入 任何,在 Map<String,Any>
中说,然后检索它们,将它们匹配并 downcast/assign 到函数变量并使用它。问题是泛型做 Type Erasure,所以如果你存储一个函数到 Any 你可以'不要取回并使用它,即将它与 Function2<Int,Int,Int>
匹配,实际类型将是:
Function2<*,*,*>
并且无法使用,因为它只匹配类型为 Nothing 的参数的签名,即你已经完成了。 (唯一可以使用它的情况是 Function0
因为它没有参数或 return 类型。)
似乎编译类型函数签名和运行时函数签名之间存在脱节。
我设法用下面的代码克服了这个问题,我讨厌它,因为样板和我必须使用 class 继承来键入函数签名的事实。我知道转换需要代码对存储的对象有额外的了解并且可以被认为是一种反模式,但是如何存储功能以供以后动态调用? (可能有其他数据会匹配 app/server 中的参数和类型,其他模块都会声明它们自己的函数。在下面的示例中,classes 只是声明一个绑定到签名的函数类型,在一个完整的示例中,classes 将在构造函数中采用实际的 lambda,即 Function2IntToInt
的每个实例都指向另一个函数,因此它有 2 层深:签名和函数指针。
感谢您的宝贵意见和建议。
编辑:澄清和拼写
// used as receiver object
class Zar(var v:Int) {}
// base class to represent all functions
open class Func
// 2 function examples, all function signatures will be 1 level deep inheritance.
class Function2IntToInt : Func() {
val func = fun Zar.(x:Int,y:Int): Int { return x+y+v}
}
//clarification: fixed example used above, could also have been:
//class Function2IntToInt(var func: Zar.(Int,Int)->Int) : Func() {}
class FunctionStringToInt : Func() {
val func = fun (s:String): Int { return s.length}
}
fun main() {
var a = Zar(1)
// these 2 could be stored into a collection of type Map<String,Func>
var func:Func = Function2IntToInt()
var func2:Func = FunctionStringToInt()
// function retrieval
var func3:Func = func
// casting. It could fail if function storage was mismatched, but not here.
var iface = func3 as Function2IntToInt
var f = iface.func
println("Invocation: result must match 3: " + a.f(1,1))
}
由于 JVM 的类型擦除,存储在多个子类型集合中的任何类型都将面临同样的问题。在这种情况下,铸造是不可避免的。对于函数,您必须创建 higher-kind 包装器类型,就像您必须能够按类型检索它们一样。您可以 fool-proof 通过按类型将项目存储在单独的列表中,在以类型为键的映射中,它会更好。
val foo = mutableMapOf<Func, MutableMap<String, Func>>()
fun <T: Func> storeItem(item: T, name: String) {
foo.getOrPut(bar::class, ::mutableMapOf)[name] = item
}
inline fun <reified T: Func> retrieveItem(name: String): T? {
return foo[T::class]?.get(name) as T?
}
请注意,您并非严格需要 Func
超类型。您可以在上面的代码中将其替换为 Any 并使您的实现直接从它们的功能定义继承:
abstract class FunctionStringToInt: (String) -> Int
class MyStringToIntImpl: FunctionStringToInt() {
override fun invoke(s: String): Int {
return s.length
}
}
看来我在这里遗漏了一些非常重要的东西。 Kotlin 函数类型使用称为 FunctionN 的特定 classes 实现,并使用泛型定义接收器、参数和 return类型。存储函数引用时是否有语法可以保留函数签名?或者任何其他方式来执行我在下面的代码中所做的事情?
那是因为这一切都是乐趣和游戏,直到您希望 实际 存储 他们输入 任何,在 Map<String,Any>
中说,然后检索它们,将它们匹配并 downcast/assign 到函数变量并使用它。问题是泛型做 Type Erasure,所以如果你存储一个函数到 Any 你可以'不要取回并使用它,即将它与 Function2<Int,Int,Int>
匹配,实际类型将是:
Function2<*,*,*>
并且无法使用,因为它只匹配类型为 Nothing 的参数的签名,即你已经完成了。 (唯一可以使用它的情况是 Function0
因为它没有参数或 return 类型。)
似乎编译类型函数签名和运行时函数签名之间存在脱节。
我设法用下面的代码克服了这个问题,我讨厌它,因为样板和我必须使用 class 继承来键入函数签名的事实。我知道转换需要代码对存储的对象有额外的了解并且可以被认为是一种反模式,但是如何存储功能以供以后动态调用? (可能有其他数据会匹配 app/server 中的参数和类型,其他模块都会声明它们自己的函数。在下面的示例中,classes 只是声明一个绑定到签名的函数类型,在一个完整的示例中,classes 将在构造函数中采用实际的 lambda,即 Function2IntToInt
的每个实例都指向另一个函数,因此它有 2 层深:签名和函数指针。
感谢您的宝贵意见和建议。
编辑:澄清和拼写
// used as receiver object
class Zar(var v:Int) {}
// base class to represent all functions
open class Func
// 2 function examples, all function signatures will be 1 level deep inheritance.
class Function2IntToInt : Func() {
val func = fun Zar.(x:Int,y:Int): Int { return x+y+v}
}
//clarification: fixed example used above, could also have been:
//class Function2IntToInt(var func: Zar.(Int,Int)->Int) : Func() {}
class FunctionStringToInt : Func() {
val func = fun (s:String): Int { return s.length}
}
fun main() {
var a = Zar(1)
// these 2 could be stored into a collection of type Map<String,Func>
var func:Func = Function2IntToInt()
var func2:Func = FunctionStringToInt()
// function retrieval
var func3:Func = func
// casting. It could fail if function storage was mismatched, but not here.
var iface = func3 as Function2IntToInt
var f = iface.func
println("Invocation: result must match 3: " + a.f(1,1))
}
由于 JVM 的类型擦除,存储在多个子类型集合中的任何类型都将面临同样的问题。在这种情况下,铸造是不可避免的。对于函数,您必须创建 higher-kind 包装器类型,就像您必须能够按类型检索它们一样。您可以 fool-proof 通过按类型将项目存储在单独的列表中,在以类型为键的映射中,它会更好。
val foo = mutableMapOf<Func, MutableMap<String, Func>>()
fun <T: Func> storeItem(item: T, name: String) {
foo.getOrPut(bar::class, ::mutableMapOf)[name] = item
}
inline fun <reified T: Func> retrieveItem(name: String): T? {
return foo[T::class]?.get(name) as T?
}
请注意,您并非严格需要 Func
超类型。您可以在上面的代码中将其替换为 Any 并使您的实现直接从它们的功能定义继承:
abstract class FunctionStringToInt: (String) -> Int
class MyStringToIntImpl: FunctionStringToInt() {
override fun invoke(s: String): Int {
return s.length
}
}