英文:
golang: how is "func() interface {}" and "func() *MyStruct" incompatible types?
问题
假设我有一段代码,其中一个函数接受另一个函数作为参数:
type Person struct {
Name string
}
func personBuilder() *Person {
return &Person{Name: "John"}
}
func printRetrievedItem(callback func() interface{}) {
fmt.Print(callback())
}
func doStuff() {
printRetrievedItem(personBuilder)
}
这会导致错误cannot use personBuilder (type func() *Person) as type func() interface {} in function argument
。如果我将personBuilder
的返回类型更改为interface{}
,它就可以工作了,但在我正在处理的实际项目中,我希望有一个明确的类型以便进行清晰的设计和TDD。
Go是否支持这种方法签名的泛化?如果无法更改personBuilder
部分(例如,您有许多无参数函数返回不同类型的结构体,并且您希望构建一个接受任何这些构建器作为参数的消费函数),有哪些解决方法?
英文:
Suppose I have code, where a function accepts another one as an argument:
<!-- language-all: Go -->
type Person struct {
Name string
}
func personBuilder() * Person {
return &Person{Name: "John"}
}
func printRetrievedItem(callback func() interface {}){
fmt.Print(callback());
}
func doStuff(){
printRetrievedItem(personBuilder);
}
This results in error cannot use personBuilder (type func() *Person) as type func() interface {} in function argument
. If I change personBuilder
return type to interface{}
, it works, but in real project I'm working on I want to have a concrete type for clear design and TDD purposes.
Does Go support such method signature generalization? What are the workarounds, if you could not change the personBuilder
part (e.g. you have a lot parameterless functions that return different type of struct, and you want to build a consumer function that accepts any of those builders as argument)?
答案1
得分: 4
一个解决方法是定义一个内联函数来调用 personBuilder
。
printRetrievedItem(func() interface{} {return personBuilder()});
<kbd>Playground</kbd>
英文:
One workaround is to define an inline function that calls personBuilder
.
printRetrievedItem(func() interface{} {return personBuilder()});
<kbd>Playground</kbd>
答案2
得分: 4
你可以创建一个带有返回interface{}
的方法的接口:
type Thinger interface {
Thing() interface{}
}
func (p *Person) Thing() interface{} {
return p
}
func printRetrievedItem(t Thinger){
fmt.Print(t.Thing());
}
func doStuff(){
printRetrievedItem(personBuilder);
}
这只是一个示例,请使用更好的名称!
回答你的问题,fun() A
不是func() interface{}
,原因与[]A
不是[]interface{}
相同。在Go Wiki中有很好的解释。
英文:
You can create an interface with a method that returns an interface{}
:
type Thinger interface {
Thing() interface{}
}
func (p *Person) Thing() interface{} {
return p
}
func printRetrievedItem(t Thinger){
fmt.Print(t.Thing());
}
func doStuff(){
printRetrievedItem(personBuilder);
}
This is just an example, please use a better name!
To answer your question, fun() A
is not a func() interface{}
, for the same reason that []A
is not an []interface{}
. It's explained very well in the go wiki.
答案3
得分: 3
你可以像@GrzegorzŻur建议的那样创建一个包装器,或者定义自己的接口并使你的xxxxBuilder
返回它:
type Namer interface {
Name() string
}
type Person struct {
name string
}
func (p *Person) Name() string {
return p.name
}
func personBuilder() Namer {
return &Person{name: "John"}
}
func printRetrievedItem(callback func() Namer) {
fmt.Printf("%T: %v", callback(), callback().Name())
}
英文:
Either do a wrapper like @GrzegorzŻur suggested or define your own interface and make your xxxxBuilder
return it:
type Namer interface {
Name() string
}
type Person struct {
name string
}
func (p *Person) Name() string {
return p.name
}
func personBuilder() Namer {
return &Person{name: "John"}
}
func printRetrievedItem(callback func() Namer) {
fmt.Printf("%T: %v", callback(), callback().Name())
}
答案4
得分: 1
你可以使用pkg reflect
来实现这个功能。(但请注意,@OneOfOne的解决方案更符合惯用写法)。
package main
import (
"fmt"
"reflect"
)
type Person struct {
Name string
}
func personBuilder() *Person {
return &Person{Name: "John"}
}
func printRetrievedItem(callback interface{}) {
vals := reflect.ValueOf(callback).Call([]reflect.Value{})
fmt.Println(vals[0].Interface())
}
func main() {
printRetrievedItem(personBuilder) // &{John}
printRetrievedItem(func() string { return "hello" }) // hello
}
这里是一个在playground上的示例。
英文:
You can use pkg reflect
for this. (Note however that the solution of @OneOfOne is more idiomatic).
package main
import (
"fmt"
"reflect"
)
type Person struct {
Name string
}
func personBuilder() *Person {
return &Person{Name: "John"}
}
func printRetrievedItem(callback interface{}) {
vals := reflect.ValueOf(callback).Call([]reflect.Value{})
fmt.Println(vals[0].Interface())
}
func main() {
printRetrievedItem(personBuilder) // &{John}
printRetrievedItem(func() string { return "hello" }) // hello
}
Here's an example in the playground.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论