【问题标题】:golang: how is "func() interface {}" and "func() *MyStruct" incompatible types?golang:“func() interface {}”和“func() *MyStruct”不兼容的类型如何?
【发布时间】:2023-03-28 07:32:01
【问题描述】:

假设我有代码,其中一个函数接受另一个函数作为参数:

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 部分(例如,您有许多返回不同类型结构的无参数函数,并且您想构建一个接受任何这些构建器作为参数的消费者函数),有什么解决方法?

【问题讨论】:

    标签: go


    【解决方案1】:

    一种解决方法是定义一个调用personBuilder 的内联函数。

    printRetrievedItem(func() interface{} {return personBuilder()});
    

    Playground

    【讨论】:

      【解决方案2】:

      要么像@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())
      }
      

      【讨论】:

      • 是的,但就我而言,我不知道对象的通用接口(消费者函数只是将接收到的对象传递给基于反射的第三方函数,例如 JSON 序列化)。
      【解决方案3】:

      您可以使用返回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里解释的很好。

      【讨论】:

      • 是的,虽然关于那部分,但在我的特殊情况下,我想将项目生产者和项目消费者分离。我不想强制项目生产者实现特殊接口,以便消费者可以使用它。感谢您对兼容性的解释。
      【解决方案4】:

      您可以为此使用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
      }
      

      Here 是操场上的一个例子。

      【讨论】:

      • 嗯,是的,解决了我的问题,但显然不是用于产品代码的优雅代码。也许我会重新考虑一下应用程序的总体结构(使用通道在生产者和消费者之间交换对象?不知道)
      • @uiron 同意了。只是想证明它在技术上是可行的:-)
      • @jabclab 请记住,通过反射调用函数非常慢。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-08
      • 2016-11-13
      相关资源
      最近更新 更多