Reputation: 8313
I'm trying to firm up the concept of inheritence that Go provides (rather "composition" than pure inheritence, perhaps). However, I'm failing to grasp why I can't use the "parent" type as a func
parameter to produce a generic function that acts on the parameter.
package main
import "log"
type Animal struct {
Colour string
Name string
}
type Dog struct {
Animal
}
func PrintColour(a *Animal) {
log.Printf("%s\n", a.Colour)
}
func main () {
a := new (Animal)
a.Colour = "Void"
d := new (Dog)
d.Colour = "Black"
PrintColour(a)
PrintColour(d)
}
Assuming my understanding's incorrect, how can I achieve what I want in Go?
Edit Note:
I don't want to attach the behaviour to the struct
I'd like to keep the pointer type as the method parameter because I'm working separately on a pet project and this requires I manipulate the struct passed in before then acting on it.
In reality my Dog
struct would have additional fields/members; hopefully this doesn't muddy the water further
Upvotes: 8
Views: 12640
Reputation: 9622
I like the answers here so far and I want to add one that allows you to do static type checking on the interface you pass in using an interface:
package main
import (
"fmt"
)
type Animalizer interface {
GetColour() string
}
type Animal struct {
Colour string
Name string
}
type Dog struct {
Animal
}
func (a *Animal) GetColour() string {
return a.Colour
}
func PrintColour(a Animalizer) {
fmt.Print(a.GetColour())
}
func main() {
a := new(Animal)
a.Colour = "Void"
d := new(Dog)
d.Colour = "Black"
PrintColour(a)
PrintColour(d)
}
It will be possible to add further fields to Dog
. The difference to Uriel's Answer is that calls to PrintColour
will fail at compile time if something else than a struct implementing Animalizer
is passed in.
Also you won't have to use a typeswitch since the compiler knows an Animalizer
is implementing GetColour
.
And, finally, the behaviour (printing) is separated from the struct, GetColour
just returns the colour.
Upvotes: 13
Reputation: 8885
My example might not be great but you can do what you want this way:
http://play.golang.org/p/JoAlOvJthr
Essentially use an interface to define the common functionality you want to expose to the outside world for all your types and embedded types.
(My example may not be the best but it works)
Upvotes: 1
Reputation: 52176
The embedded (anonymous) field can still be explicitly accessed by using its typename :
package main
import "log"
type Animal struct {
Colour string
Name string
}
type Dog struct {
Animal
}
func PrintColour(a *Animal) {
log.Printf("%s\n", a.Colour)
}
func main() {
a := new(Animal)
a.Colour = "Void"
PrintColour(a)
d := new(Dog)
d.Colour = "Black"
// you can access the underlying "Animal" through "d.Animal"
PrintColour(&d.Animal)
}
In the reference : the sentence after the second code block explains how you can declare an "anonymous" field, and states :
The unqualified type name acts as the field name.
Upvotes: 1
Reputation: 1934
You could try it with interface{}
package main
import ("fmt"
"reflect")
type Animal struct {
Colour string
Name string
}
type Dog struct {
Animal
}
func PrintColour(a interface{}) {
switch a.(type){
case *Dog:
fmt.Printf("Dog %s\n", a.(*Dog).Colour)
case *Animal:
fmt.Printf("Aimal %s\n", a.(*Animal).Colour)
default:
fmt.Printf("hmm %s\n", reflect.TypeOf(a))
}
}
func main () {
a := new (Animal)
a.Colour = "Void"
d := new (Dog)
d.Colour = "Black"
PrintColour(a)
PrintColour(d)
}
Upvotes: 3
Reputation: 19418
If you declare the PrintColour
method on the Animal
type, it will be "inherited" when you include Animal
in Dog
.
This is known as "Embedding" in the Go world. See The "Embedding" section of Effective Go for more info.
Try something like:
package main
import "log"
type Animal struct {
Colour string
Name string
}
type Dog struct {
Animal
}
func (a *Animal) PrintColour() {
log.Printf("%s\n", a.Colour)
}
func main () {
a := new (Animal)
a.Colour = "Void"
d := new (Dog)
d.Colour = "Black"
a.PrintColour()
d.PrintColour()
}
Produces:
2009/11/10 23:00:00 Void
2009/11/10 23:00:00 Black
Upvotes: 4