Golang struct calling embedded type methods when method has been overloaded

Go

Go Problem Overview


I am trying to learn Go, and I found a good resource here.

The example given on method overloading is reproduced below:

package main
import "fmt"

type Human struct {
    name string
    age int
    phone string
}


type Employee struct {
    Human 
    company string
}

func (h *Human) SayHi() {
    fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}

func (e *Employee) SayHi() {
    fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
        e.company, e.phone) //Yes you can split into 2 lines here.
}

func main() {
    sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
    sam.SayHi()
}

Is it possible to call the "base" struct's (Human's) methods, eg. sam.Human.SayHi() Downcasting doesn't work (because there is no type hierarchy right?)

Go Solutions


Solution 1 - Go

You can access the embedded struct of a parent struct by calling the member of the parent with the name of the embedded type's name. That's a mouthful, so it's probably easier to demonstrate it.

 sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
 sam.SayHi() // calls Employee.SayHi
 sam.Human.SayHi() // calls Human.SayHi

Outputs

 Hi, I am Sam, I work at Golang Inc. Call me on 111-888-XXXX
 Hi, I am Sam you can call me on 111-888-XXXX

Solution 2 - Go

This is the nearest approximation to sane polymorphism with both plain and pure virtual functions that I have found thus far. By the very nature of Go's design and the goal at hand, it is ugly but effective.

package main

import (
	"fmt"
)

type I interface {
	foo(s string)	// Our "pure virtual" function
	bar()
}

type A struct {i I}
type B struct {A}
type C struct {B}

// fk receivers, this is a "member function" so I'll use OO nomenclature
func (this *A) init(i I) {
	this.i = i	// the i contains (internal) pointers to both an object and a type
}

func (this *A) bar() {
	this.i.foo("world")
}

func (this *B) foo(s string) {
	fmt.Printf("hello %s\n", s)
}

func (this *C) foo(s string) {
	fmt.Printf("goodbye cruel %s\n", s)
}

func main() {
	var i I
	b := &B{}
	b.init(b)	// passing b as the parameter implicitly casts it to an I interface object
	b.bar()
	c := &C{}
	c.init(c)
	c.bar()		// c is a pointer to C, so Golang calls the correct receiver

	i = b
	i.bar()
	i = c
	i.bar()		// Internally, i contains pointers to the C object and the C type,
			// so that the correct receiver is called
}

https://play.golang.org/p/4qBfmJgyuHC

In real OO languages, each object of a class with any virtual functions must have a pointer to a virtual function table or a least type that maps to it. So adding an interface member to the base (embedded) struct only wastes an additional machine word for the pointer that will just point to its self.

Alternatively, we could remove the I interface member from A and the have pure virtual member function just accept the implementation as an argument.

type I interface {
	foo(s string)
	bar(i I)
}

type A struct {}
type B struct {A}
type C struct {B}

func (this *A) bar(i I) {
	i.foo("world")
}

https://play.golang.org/p/9gvaCuqmHS8

But at this point, foo is no longer a pure virtual function, API users could pass any value whose type implements I, and the whole OO concept is broken. The object pointer portion of the I interface passed to bar doesn't need to be the same as this, but then again we didn't need to pass that same value using an init() function, but at least only an API user in the same package would be allowed to set it.

At this point, we have transitioned to the Go way of doing things: compositional programming with dependency injection. This is what Ken Thompson and the other designers thought was a better way to go -- at least for their stated goals. While it is vastly inferior in MANY respects, it does create many advantages and I won't argue those points that here.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLeeView Question on Stackoverflow
Solution 1 - GoHunter FernandesView Answer on Stackoverflow
Solution 2 - GoDaniel SantosView Answer on Stackoverflow