Reputation: 1005
Clarification: I'm just learning GO, and came across this problem.
I'm trying to implement a "class" that inherit a method that invokes a "virtual" method that should be implemented by the child class. Here's my code:
https://play.golang.org/p/ysiaPwARkvl
package main
import (
"fmt"
"sync"
)
type Parent struct {
sync.Mutex
MyInterface
}
func (p *Parent) Foo() {
p.Lock()
defer p.Unlock()
p.Bar()
}
func (p *Parent) B(){
panic("NOT IMPLEMENTED")
}
func (p *Parent) A() {
p.Lock()
defer p.Unlock()
p.B()
}
type MyInterface interface {
Foo()
Bar()
}
type Child struct {
Parent
Name string
}
func (c *Child) Bar(){
fmt.Println(c.Name)
}
func (c *Child) B(){
fmt.Println(c.Name)
}
func main() {
c := new(Child)
c.Name = "Child"
// c.A() panic
c.Foo() // pointer error
}
I left out some code regarding the sync.Mutex that does some async update to the values of Child.
So apparently in A() or Foo() the pointer p have type Parent. How should I change my code so that A/Foo refer to B/Bar defined in the Child class?
Upvotes: 2
Views: 9342
Reputation:
You're wanting an is-a relationship (inheritance) when Go only provides has-a relationships (composition):
Child
is not a kind of Parent
, so a pointer to a Parent
cannot retain a pointer to a Child
; Child
has-a Parent
contained within it instead.Because there is no is-a relationship between Parent
and Child
, Parent.Foo
cannot receive an object of type Child
, nor can it use any of the methods that Child
implements. Also, this means that no Parent
can access any method defined on a Child
such as Bar()
or B()
directly.
Typically, Parent
will not need to call some method in Child
. If it does, you'd pass the Parent
method an argument, such as an interface that Child
satisfies for you to call the method through the interface or a closure that calls the Child
method:
// Use of an interface that Child satisfies.
type Beta interface {
B()
}
func (p *Parent) A(b Beta) {
p.Lock()
defer p.Unlock()
b.B()
}
// Use of a closure.
func (p *Parent) Foo(bar func()) {
p.Lock()
defer p.Unlock()
bar()
}
func callFoo(p *Parent, c *Child) {
callBar := func() {
c.Bar()
}
p.Foo(callBar)
}
func (c *Child) Bar() {
// stub
}
func (c *Child) B() {
// stub
}
You get the Child
can call Parent
method behavior for free, but it only appears similar to inheritance. child.Foo()
actually performs child.Parent.Foo()
, which means Parent.Foo
still receives a Parent
instance (hence the name), not a Child
instance.
However, Parent
cannot access any information about Child
that Child
does not explicitly share. Interfaces and closures can act as mechanisms between two classes analogous to the friend
keyword in C++, except they're more restrictive than the friend
keyword. After all, Child
doesn't need to share everything with Parent
, just the bits that it wants to share, somewhat similar to this pattern in C++. Personally I would prefer interfaces for this as it allows your Parent
"class" to work with multiple types that all satisfy a common interface, making it pretty much the same as calling the method from a normal function or an entirely unrelated type's method.
Upvotes: 5