54
votes

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?)

2
Overloading is not the right term,overriding would be better, but the author used it...(probably translation issues - the original is in Chinese...) - Lee

2 Answers

79
votes

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
0
votes

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.