Go语言中将任意函数作为参数传递 无需匿名包装转换类型
结论
不能直接转换,Go的类型系统不支持func(string) string和func(interface{}) interface{}两种函数类型的直接转换,也没有语法可以跳过包装步骤实现直接传入。
原因解释
- Go语言中函数是值类型,两种函数类型要兼容的前提是参数、返回值的类型和顺序完全一致。
func(string) string的参数和返回值都是具体的string类型,和func(interface{}) interface{}的签名完全不匹配,不存在隐式转换规则,也无法通过强制类型转换语法实现转换。 - 两种函数的底层调用约定不同:调用
func(string) string时传入的是string的底层数据结构,调用func(interface{}) interface{}时传入的是interface{}的双层结构体(类型指针+数据指针),直接转换会导致运行时参数解析错误,因此Go类型系统从编译层面就禁止了这种操作。
替代方案
如果不想每次手写针对特定类型函数的匿名包装,可以通过反射实现通用的适配器函数,自动完成类型适配:
package main import ( "fmt" "reflect" ) func decorate(inner func(interface{}) interface{}, args interface{}) interface {} { fmt.Println("Before inner") result := inner(args) fmt.Println("After inner") return result } // 通用适配器:将任意单入参单返回值的函数转换为func(interface{}) interface{}类型 func adapt(f interface{}) func(interface{}) interface{} { fVal := reflect.ValueOf(f) fType := fVal.Type() // 校验入参符合要求 if fType.Kind() != reflect.Func || fType.NumIn() != 1 || fType.NumOut() != 1 { panic("adapt only accepts functions with exactly 1 input and 1 output parameter") } return func(arg interface{}) interface{} { argVal := reflect.ValueOf(arg) // 校验参数类型匹配 if !argVal.Type().AssignableTo(fType.In(0)) { panic(fmt.Sprintf("argument type mismatch: expect %s, got %s", fType.In(0), argVal.Type())) } res := fVal.Call([]reflect.Value{argVal}) return res[0].Interface() } } func funcA(arg interface{}) interface{} { fmt.Print("Inside A, with arg: ") fmt.Println(arg) return "This is A's return value" } func funcB(arg string) string { fmt.Print("Inside B, with arg: ") fmt.Println(arg) return "This is B's return value" } func main() { fmt.Println(decorate(funcA, "(This is A's argument)")) // 传入funcB时只需要套一层adapt即可,不需要手写匿名包装 fmt.Println(decorate(adapt(funcB), "(This is B's argument)")) }
运行后可以得到预期输出:
Before inner Inside A, with arg: (This is A's argument) After inner This is A's return value Before inner Inside B, with arg: (This is B's argument) After inner This is B's return value
注意:该方案本质还是自动生成了匿名包装函数,只是不需要开发者手动编写而已,不存在完全跳过包装的实现方式。
内容的提问来源于stack exchange,提问作者riwalk
相关产品推荐
相关产品推荐

