函数类型中的空接口{}

huangapple go评论89阅读模式
英文:

Empty interface{} in function type

问题

任何类型的对象都可以赋值给一个空接口。例如,我们有以下函数:

func Println(i interface{}) {
  fmt.Println(i)
}

我们可以这样调用它:

Println(3)
Println(1.5)
Println("Hello")

但是对于函数类型,我无法实现相同的效果:

func Map(fn func(interface{})) {
  ......
}

我不能这样调用它:

Map(func(i int) {......})

因为类型 func(int) 与类型 func(interface{}) 不同。

但是当我定义 func(interface{}) 时,我真正的意思是任何类型的输入参数。在Go语言中,我该如何实现这一点呢?

英文:

An object of any type can be assigned to an empty interface. For example, we have the following function

func Println(i interface{} ) {
  fmt.Println(i)
}

We can call it by

Println(3)
Println(1.5)
Println("Hello")

But I can't achieve the same thing for function type

func Map(fn func( interface{} )) {  
  ......  
}  

I cannot call this with
Map( func( i int) {......} )
because the type func(int) is different from the type func( interface{} ).

But when I define func( interface{} ), I really mean any type of the input parameters. How can I achieve this in Go?

答案1

得分: 13

它失败是因为签名不匹配。

当你调用Println(3)时,函数的第一个参数并不是一个整数。相反,整数被打包在一个interface{}变量中(这是自动转换,因为整数符合接口),然后将该变量传递给函数。这个转换发生在调用方,所以调用函数的过程与调用匹配func(i int)的函数不同。

如果你想编写一个接受任意一元函数的函数,你需要声明它以一个interface{}变量作为参数,然后使用reflect包来检查值。reflect包还可以帮助你调用在编译时不知道签名的任意函数。

例如:

func Map(f, v interface{}) interface{} {
    fn := reflect.ValueOf(f)
    fnType := fn.Type()
    if fnType.Kind() != reflect.Func || fnType.NumIn() != 1 || fnType.NumOut() != 1 {
        panic("Expected a unary function returning a single value")
    }
    res := fn.Call([]reflect.Value{reflect.ValueOf(v)})
    return res[0].Interface()
}

这将使用参数v调用给定的函数f并返回结果。只要v可以赋值给f的第一个参数,调用就会成功,不会引发panic。你可以在这里尝试这个例子:http://play.golang.org/p/kkBu56JYb8

英文:

It fails because the signatures don't match.

When you call Println(3), the function isn't taking an integer as its first argument. Rather the integer gets packed inside an interface{} variable (an automatic conversion, since integers conform to the interface), and that variable is passed to the function. This conversion happens on the calling side, so the process of calling the function is different to calling a function matching func(i int).

If you want to write a function that accepts arbitrary unary functions, you will need to declare it to take an interface{} variable as its argument and then check the value using the reflect package. The reflect package can also help you call arbitrary functions where you don't know the signature at compile time.

For example:

func Map(f, v interface{}) interface{} {
	fn := reflect.ValueOf(f)
	fnType := fn.Type()
	if fnType.Kind() != reflect.Func || fnType.NumIn() != 1 || fnType.NumOut() != 1 {
		panic("Expected a unary function returning a single value")
	}
	res := fn.Call([]reflect.Value{reflect.ValueOf(v)})
	return res[0].Interface()
}

This will call the given function f with the argument v and return the result. Provided v is assignable to f's first argument the call will succeed without a panic. You can experiment with this example here: http://play.golang.org/p/kkBu56JYb8

答案2

得分: 1

我意识到这是一个旧的讨论,但我遇到了这篇帖子,并想尝试一下在另一个函数中使用任意函数func (interface{})的概念,而不是interface{}

我可以通过提供一个内联函数的实现来编写一个简单的实现,该函数将接受interface{}。然后我们可以从另一个函数中调用这个函数。

varForGenFunc := func(in interface{}) int {								
    fmt.Println("type of this object: ", reflect.TypeOf(in))
    return 1
}

TakeGenericFunc(varForGenFunc, variableForGen)

根据这个例子,我们可以编写任何func(interface{})的实现,并将其作为参数传递给TakeGenericFunc函数。

你可以在这里尝试一下:

https://play.golang.org/p/f5UUhyhEx7u

英文:

I do realise its an old discussion, but came across the post and wanted to play around with the concept of having arbitrary function func (interface{}) within another function, instead of interface{}.
I could write a simple implementation, by providing an inline implementation of a function which would accept interface{}. And we can call this function from within another function

varForGenFunc := func(in interface{}) int {								
					fmt.Println("type of this object: ",reflect.TypeOf(in))
					return 1}

TakeGenericFunc(varForGenFunc, variableForGen)

Going by this, we can write any implementations of func(interface{}) and pass it as parameter to TakeGenericFunc

You can play around with it here:

https://play.golang.org/p/f5UUhyhEx7u

huangapple
  • 本文由 发表于 2014年10月1日 14:18:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/26135227.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定