【问题标题】:function to return an Interface返回接口的函数
【发布时间】:2016-01-26 03:32:25
【问题描述】:

为什么我可以说 CreateLion() 的结果,一个指向实现 Cat 接口的结构的指针,是 Cat 接口的一个实例,但我不能说 CreateLion() 是类型“函数返回 Cat 接口。”

实现这种行为的标准 Golang 方法是什么?

package main

import "fmt"

func main() {
    var lion Cat := CreateLion()
    lion.Meow()

    // this line breaks. Why?
    var cf CatFactory = CreateLion
}

type Cat interface {
    Meow()
}

type Lion struct {}
func (l Lion) Meow() {
    fmt.Println("Roar")
}

// define a functor that returns a Cat interface
type CatFactory func() Cat

// define a function that returns a pointer to a Lion struct
func CreateLion() *Lion {
    return &Lion{}
}

【问题讨论】:

  • Go 不是 Java。 Go 没有继承,也没有“是”的概念。您对问题建模的尝试看起来像是从 Java 到 Go 的直接翻译。这行不通。从技术上讲,joy miao 的回答是一个完整而明智的解释。 Go 是静态类型的,*Lion 不是Cat,即使*Lion 可以分配给Cat 类型的变量;但是在匹配类型签名时可分配性不起作用。 Go 中不需要工厂。看看 stdlib 是怎么做的,别再用 Go 写 Java 代码了。
  • 使用函数作为类型并不是 Java 独有的,@Volker。此代码不是从 Java 翻译过来的。
  • 稍作改动就可以正常工作:play.golang.org/p/ECSpoOIuzEx 继承并不是真正的问题。 Go 的 OO 构造主题较小,就像 Java 相对于 C++ 具有较小的子集一样

标签: go


【解决方案1】:

试试这个:

package main

import "fmt"

type Cat interface {
    Meow()
}

type Lion struct{}

func (l Lion) Meow() {
    fmt.Println("Roar")
}

type CatFactory func() Cat

func CreateLion() Cat {
    return Lion{}
}

func main() {
    lion := CreateLion()
    lion.Meow()

    var cf CatFactory = CreateLion
    fLion := cf()
    fLion.Meow()
}

在大多数情况下,您可以将任何类型分配给基本类型interface{}。但是如果函数参数的类型是map[T]interface{}[]interface{}func() interface{},情况就会发生变化。 在这种情况下,类型必须相同。

【讨论】:

  • 当然你的代码可以工作,但这不是一个好的答案,他不是在寻找工作代码,他想要一个解释。
  • 在我的用例中,Cat 接口是在一个单独的 go 包中定义的,我不希望将其导入到定义了 Lion 结构的 go 包中。所以我不能像你建议的那样改变 CreateLion 函数的返回类型。
  • 您不能将 func struct 分配给 func interface{}。情况类似[]interface{} - []struct。因为它是不同的类型。
  • 这个答案很糟糕,因为现在 CreateLion 返回一只猫,并且您不再有权访问任何特定于 Lion 的行为。 CatFactory 返回一只猫是有意义的,但 CreateLion 应该返回一只狮子。
  • CreateLion 返回Lion) 但是返回值的接口是一只猫(Lion 是一只大猫)
【解决方案2】:

我认为你应该阅读这个博客http://blog.golang.org/laws-of-reflection,它对变量、类型和接口之间的关系进行了精确的描述。

在您的示例中,*LionCat 不同。

您可以将函数 CreateLion*Lion 返回更正为 Cat

【讨论】:

    【解决方案3】:

    这里的问题是静态类型的 go 将“这是一个返回猫的函数”与“这是一个返回一只猫的狮子的函数”区分开来,因此不会接受一个作为另一个。

    解决这个问题的方法是让您的工厂变量完全符合它的期望:

    var cf CatFactory = func() Cat{
        return CreateLion()
    }
    catlion := cf()
    catlion.Meow()
    

    【讨论】:

      【解决方案4】:

      稍作改动即可正常工作。在这里查看:https://play.golang.org/p/ECSpoOIuzEx

      package main
      
      import "fmt"
      
      func main() {
          lion := CreateLion() // Go idomatic style recommends 
                               // allowing the compiler to divine the type
          lion.Meow()
      
          CatFactory := CreateLion
          _ = CatFactory // Go doesn't like unused variables and fails the build
      
          obj := CatFactory()     // exercising our factory method
          obj.Meow()
      }
      
      type Cat interface {
          Meow()
      }
      
      type Lion struct {}
      func (l Lion) Meow() {
          fmt.Println("Roar")
      }
      
      // define a functor that returns a Cat interface
      type CatFactory func() Cat
      
      // define a function that returns a pointer to a Lion struct
      func CreateLion() *Lion {
          return &Lion{}
      }
      

      另外,虽然 Go 没有 Java 风格的接口,但它确实有接口并且你可以实现多态性,但是类型在编译时是已知的。

      如果两种类型都实现相同的接口,您可以对“Is A”关系建模。但是,在您将对象传递给接受该接口类型的函数之前,它不会强制执行该接口。因此,如果您想象实现策略模式,当您传入策略对象匹配接口“Cat”时,该函数将接受“Lion”对象,或任何其他实现具有正确签名的 Meow 函数的类。

      此外,工厂方法在 Go 中绝对是必要且有用的。事实上,在 Go 中,您使用工厂函数来构造对象,而不是构造函数。

      【讨论】:

      • func CreateLion() Lion {return Lion{}} 也可以。很好的答案 +1。
      • 您只是在对变量/类型进行着色。只需删除type CatFactory func () Cat。它没有在您的代码中使用。
      猜你喜欢
      • 2019-09-27
      • 2019-12-17
      • 2015-06-01
      • 2018-07-12
      • 2018-03-22
      • 2015-02-05
      • 2019-01-18
      • 2021-12-21
      • 1970-01-01
      相关资源
      最近更新 更多