“golang: `func() interface{}`和`func() *MyStruct`为什么是不兼容的类型?”

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

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 &amp;Person{Name: &quot;John&quot;}
}

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 &amp;Person{name: &quot;John&quot;}
}

func printRetrievedItem(callback func() Namer) {
	fmt.Printf(&quot;%T: %v&quot;, 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 (
    &quot;fmt&quot;
    &quot;reflect&quot;
)

type Person struct {
    Name string
}

func personBuilder() *Person {
    return &amp;Person{Name: &quot;John&quot;}
}

func printRetrievedItem(callback interface{}) {
    vals := reflect.ValueOf(callback).Call([]reflect.Value{})
    fmt.Println(vals[0].Interface())
}

func main() {
    printRetrievedItem(personBuilder) // &amp;{John}
    printRetrievedItem(func() string { return &quot;hello&quot; }) // hello
}

Here's an example in the playground.

huangapple
  • 本文由 发表于 2015年7月14日 20:41:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/31406892.html
匿名

发表评论

匿名网友

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

确定