user1948847
user1948847

Reputation: 1005

Golang Inheritance and method override

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

Answers (1)

user539810
user539810

Reputation:

You're wanting an is-a relationship (inheritance) when Go only provides has-a relationships (composition):

  • Go has no inheritance, thus there is no is-a relationship between two types. 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

Related Questions