I am trying understanding Google Go's embedding mechanism (as an alternative to subclassing). Below is a simple program that summarizes my problem with the approach:
package main
import "fmt"
type Person struct {
Name string
}
func (p *Person) Talk() {
fmt.Println("Hi, my name is Person")
}
func (p *Person) TalkVia() {
fmt.Println("TalkVia ->")
p.Talk()
}
type Android struct {
Person
}
func (p *Android) Talk() {
fmt.Println("Hi, my name is Android")
}
func main() {
fmt.Println("Person")
p := new(Person)
p.Talk()
p.TalkVia()
fmt.Println("Android")
a := new(Android)
a.Talk()
a.TalkVia()
}
The output is:
Person
Hi, my name is Person
TalkVia ->
Hi, my name is Person
Android
Hi, my name is Android
TalkVia ->
Hi, my name is Person
but if I was subclassing (in another language), the output will be:
Person
Hi, my name is Person
TalkVia ->
Hi, my name is Person
Android
Hi, my name is Android
TalkVia ->
Hi, my name is Android
Is there a way to achieve this last output with google go embedding (not interfaces)?
No. Embedding is a one way thing. Embedding is slightly too unique to merely call it "syntactic sugar" -- it can be used to satisfy interfaces. Android should satisfy the interface
type TalkViaer interface {
TalkVia()
}
because it embeds a person. However, at its heart you have to remember that embedding is just a really clever way of giving access to a struct's member. Nothing more or less. When p
is passed into TalkVia
it gets a Person, and since that person has no conception of its owner, it won't be able to reference its owner.
You can work around this by holding some owner variable in Person
, but embedding is not inheritance. There's simply no conception of a "super" or an "extender" or anything like that. It's just a very convenient way to give a struct a certain method set.
Edit: Perhaps a little more explanation is in order. But just a little.
type Android struct {
P person
}
We both agree that if I did a := Android{}
and then a.P.TalkVia()
it wouldn't call any of Android's methods, right? Even if that was Java or C++, that wouldn't make sense, since it's a member.
Embedding is still just a member. It's just a piece of data owned by the Android, no more, no less. At a syntactic level, it confers all of its methods to Android, but it's still just a member and you can't change that.