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.
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: