如何让golang接口实现返回另一个接口的实现?

3

具体示例在这里:https://play.golang.org/p/ZNmviKWLwe

我有一个接口A。我期望所有实现A的类型都具有某个特定签名(因此使用接口)。我期望其中一个返回实现另一个接口的有用内容。

type Box interface {
    GetToy(int) (*Toy, error)
}
type Toy interface{
    Play()
}


type CarBox struct {
   Length int
   Width int
   Height int
   toys []*Matchbox
}
type Matchbox struct {}

func (b *CarBox) Size () int {
    return b.Length*b.Width*b.Height
}
func (b *CarBox) GetToy(j int) (*Matchbox, error) {
    return b.toys[j], nil
}
func (m *Matchbox) Play() {
    fmt.Println("Zoom!")
}

当然,这种方法是行不通的,因为GetToy()返回的是*Matchbox而不是*Toy,即使MatchboxToy的一个完美的实现。并且,不,这不是因为指针的问题;无论我在GetToy中返回*Toy还是只返回Toy,结果都是相同的:

tmp/sandbox721971102/main.go:33:15: cannot use CarBox literal (type CarBox) as type Box in return argument:
    CarBox does not implement Box (wrong type for GetToy method)
        have GetToy(int) (*Matchbox, error)
        want GetToy(int) (*Toy, error)

显然我的模式与Go语言的工作方式不匹配。有什么“正确”的方法来解决这个问题吗?


这里最好的建议之一可能是:不要试图在Go中编写传统风格的OOP。 - Volker
哈哈!是的,确实。我抱怨过多关于面向对象编程,但是经过足够多的年份,你会发现这种行为又回来了。 - deitch
2个回答

6

首先,你几乎从不需要一个指向接口类型的指针。因此,GetToy 方法的签名应该更改为:

type Box interface {
    GetToy(int) (Toy, error) // use Toy, not *Toy
}

请注意,接口可以通过类型隐式满足。因此,通过实现 Play() 方法,*Matchbox 隐式满足 Toy 接口(请注意 *,实现 Toy 接口的类型是指向Matchbox的指针而不是 Matchbox 本身)。
这意味着可以用类型为 *Matchbox 的值替换类型为 Toy 的值。因此,需要修复 *CarBox 上定义的 GetToy 方法:
func (b *CarBox) GetToy(j int) (Toy, error) {
    return b.toys[j], nil
}

现在,GetToy将返回一个实现了Toy接口的*Matchbox

1
哦,真的吗?就这样了?我做这些东西已经很长时间了——包括在 Go 中也有一段时间——但是 Go 中指针和解引用的不连贯性仍然让我感到困惑。 - deitch
几乎了解了。显然,我需要实现func (b CarBox) GetToy(j int)而不是func (b CarBox) GetToy(j int)。https://play.golang.org/p/p6KQ5hgQmQ与https://play.golang.org/p/KtJ3NLe4DO不同。如果您更新答案,我很乐意接受它。 - deitch
在你上面的问题中,你使用 *CarBox 来满足 Box 接口。在第二个(无错误链接)中,你已经将其替换为 CarBox。我的答案是根据上面的问题。 - abhink

1

一些修复: https://play.golang.org/p/-mDr5SDZvV

指向接口的指针是极为罕见的需求。使用接口 - 它可以包含具体类型。

更新: 如果您希望拥有一个带有指针接收器的方法,例如func (b *CarBox) GetToy(j int),则可以从Tester函数返回指针(注意&):

func Tester() Box {
    return &CarBox{}
}

从Tester返回的接口值将包含对CarBox实例的指针。因此,Go运行时将能够运行具有指针接收器的方法。否则,只有一个值而不是指针。因此,只能调用具有值接收器的方法。


这是正确的,并有助于添加缺失的“&”符号。您能否提供有关返回类型和接收器类型之间关系的更多细节? - shaktisinghr
@shaktisinghr,一般来说,函数通过指针操作一个真实对象并可以更改它(指针接收器),或者获取真实对象的副本(值接收器)。在后一种情况下,您只能修改副本,因此原始对象将保持不变。如何选择使用什么?如果需要进行更改,则绝对使用指针。此外,如果您有一个非常庞大的对象,则复制将需要许多资源,因此可以使用指针。如果您有小对象,则将其作为副本传递速度更快。https://tour.golang.org/methods/8 或 https://dev.to/chen/gos-method-receiver-pointer-vs-value-1kl8 - Eugene Lisitsky
这个解释似乎是关于指针与值接收器在修改对象方面的区别。我之前的评论是关于返回类型的。 - shaktisinghr
我在代码中看到了接口类型的返回。但这并不好,因为会导致程序更加复杂。Dave Cheney在Go语言中关于SOLID原则的文章https://dave.cheney.net/2016/08/20/solid-go-design - Eugene Lisitsky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接