问题
I want to compose a type of another type, but replace one of the fields (which is an interface value) with a fake. The problem I am getting is the underlying field is being used, so I can't seem to override the field.
I've demoed the problem here: 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()
}
Why does it print "PrinterService"
? I want it to print "SomeOtherService"
.
Thanks.
回答1:
By s.PrintViaMethod()
, you are calling the promoted method FakeService.Service.PrintViaMethod()
, and the method receiver will be FakeService.Service
which is of type Service
, and Service.PrintViaMethod()
calls Service.Client.Print()
, where Service.Client
is of type which PrinterService
, that's why it prints "PrinterService"
.
In Go there is embedding, but there is no polymorphism. When you embed a type in a struct, methods of the embedded type get promoted and will be part of the method set of the embedder type. But when such a promoted method is called, it will get the embedded value as the receiver, not the embedder.
To achieve what you want, you would have to "override" the PrintViaMethod()
method by providing your implementation of it for the FakeService
type (with FakeService
receiver type), and inside it call FakeService.Client.Print()
.
By doing so s.PrintViaMethod()
will denote the FakeService.PrintViaMethod()
method as that will be at the shallowest depth where the PrintViaMethod()
exists (and not FakeService.Service.PrintViaMethod()
). This is detailed in Spec: Selectors.
For example:
func (fs FakeService) PrintViaMethod() {
fs.Client.Print()
}
Then the output will be (try it on the Go Playground):
SomeOtherService
See related questions and answers with more details:
Go embedded struct call child method instead parent method
Does fragile base class issue exist in Go?
回答2:
Why does it print 'PrinterService'? I want it to print 'SomeOtherService'.
Because that's what your code says to do. PrintViaMethod
calls s.Client.Print()
, and s.Client
is a (zero value) instance of PrinterService
, which outputs PrinterService
.
What you probably want is to call s.Print()
in main()
. I don't see any reason for your PrintByMethod
function at all.
回答3:
As per Flimzy
you are calling print s.Client.Print()
which is of type PrinterService
implemented as receiver to Print()
function printing PrinterService
. You can also change type of Client
PrinterService
in Service
struct to 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()
}
来源:https://stackoverflow.com/questions/48079775/type-composition-overriding-interface-types