8

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

5 Answers 5

13

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

On the playground

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.

Sign up to request clarification or add additional context in comments.

1 Comment

This is the approach I'd take now if I were trying this again. Thanks!
4

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

Playground

2 Comments

My problem with doing that (I'd explained I don't want to attach the func like that in the original question) is that it's a utility function and thus doesn't feel "correct" being attached to the type..I'll rethink my design though. Thanks.
On second thoughts, is there a way around this where I can pass in a parent type to the method without embedding?
3

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)

}

1 Comment

Good find, thanks. I guess I'll have to suck it up and redesign; I didn't want to "attach" behaviour to my structs (or create interfaces) as the behaviour is unrelated but alas I'll just keep practicing :)
1

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

playground

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.

1 Comment

Yep, thanks, aware of this, but it's the whole type I want as I need to "inherit" common fields (e.g. Colour) but also those specific to one type.
1

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)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.