类型组合:覆盖接口类型

时间:2018-01-03 14:52:21

标签: go struct interface embedding method-overriding

我想组成另一种类型的类型,但用假的替换其中一个字段(这是一个接口值)。我得到的问题是正在使用基础字段,所以我似乎无法覆盖该字段。

我在这里演示了这个问题:https://play.golang.org/p/lHGnyjzIS-Y

package main

import (
    "fmt"
)

type Printer interface {
    Print()
}

type PrinterService struct {}

func (ps PrinterService) Print() { fmt.Println("PrinterService") }

type Service struct {
    Client PrinterService
}

func (s Service) PrintViaMethod() { s.Client.Print() }

type FakeService struct {
    Service
    Client Printer
}

type SomeOtherService struct {}

func (sos SomeOtherService) Print() { fmt.Println("SomeOtherService") }

func main() {
    s := FakeService{Client: SomeOtherService{}}
    s.PrintViaMethod()
}

为什么打印"PrinterService"?我希望它能打印"SomeOtherService"

感谢。

3 个答案:

答案 0 :(得分:5)

s.PrintViaMethod(),您正在调用提升的方法FakeService.Service.PrintViaMethod(),方法接收方将是FakeService.Service,其类型为Service,和Service.PrintViaMethod()来电Service.Client.Print(),其中Service.Client的类型为PrinterService,这就是打印"PrinterService"的原因。

在Go中有embedding,但没有polymorphism。在结构中嵌入类型时,嵌入类型的方法会被提升,并且将成为嵌入器类型的方法集的一部分。但是当调用这样一个提升的方法时,它会将嵌入值作为接收者,而不是嵌入者。

为了实现你想要的,你必须"覆盖" PrintViaMethod()方法,为FakeService类型(FakeService接收方类型)提供实施方法,并在其中调用FakeService.Client.Print()

通过这样做s.PrintViaMethod()将表示FakeService.PrintViaMethod()方法,因为它将位于PrintViaMethod()存在的最浅深度(而不是FakeService.Service.PrintViaMethod())。详情请见Spec: Selectors

例如:

func (fs FakeService) PrintViaMethod() {
    fs.Client.Print()
}

然后输出将是(在Go Playground上尝试):

SomeOtherService

更多详细信息,请参阅相关问题和答案:

Go embedded struct call child method instead parent method

Does fragile base class issue exist in Go?

答案 1 :(得分:4)

  

为什么打印' PrinterService'?我希望它能够打印出SomeOtherService'。

因为那是您的代码所要做的。 PrintViaMethod调用s.Client.Print()s.ClientPrinterService的(零值)实例,输出PrinterService

您可能希望在s.Print()中致电main()。我根本没有看到你PrintByMethod功能的任何原因。

答案 2 :(得分:0)

根据Flimzy,您正在调用打印s.Client.Print(),其类型PrinterService已实现为Print()功能打印PrinterService的接收方。您还可以将Client结构中PrinterService Service的类型更改为Someother service

package embedded

import (
    "fmt"
)

type Printer interface {
    Print()
}

type PrinterService struct{}

func (ps PrinterService) Print() { fmt.Println("PrinterService") }

type Service struct {
    Client SomeOtherService
}

func (s Service) PrintViaMethod() { s.Client.Print() }

type FakeService struct {
    Service
    Client Printer
}

type SomeOtherService struct{}

func (sos SomeOtherService) Print() { fmt.Println("SomeOtherService") }

func Call() {
    s := FakeService{Client: SomeOtherService{}}
    s.PrintViaMethod()
}