Alex
Alex

Reputation: 8313

Go "inheritance" - using anonymous type in a struct as a method parameter

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:

Upvotes: 8

Views: 12640

Answers (5)

Beat Richartz
Beat Richartz

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

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.

Upvotes: 13

Daniel Williams
Daniel Williams

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

LeGEC
LeGEC

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

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.

Upvotes: 1

Milan Halada
Milan Halada

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

Intermernet
Intermernet

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

Playground

Upvotes: 4

Related Questions