进行“继承”-在结构中使用匿名类型作为方法参数

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
", 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

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.

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
", 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

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
", a.(*Dog).Colour)
        case *Animal:
            fmt.Printf("Aimal %s
", a.(*Animal).Colour)
        default:        
            fmt.Printf("hmm %s
", reflect.TypeOf(a))

    }
}


func main () {
    a := new (Animal)
    a.Colour = "Void"
    d := new (Dog)
    d.Colour = "Black"

    PrintColour(a)
    PrintColour(d)

}

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
", 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.

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)