Module lessons (3/5)
Composition (embedding)
Go has no inheritance. The idiomatic answer to reuse is composition, and in particular embedding: including one type inside another without giving it a field name. The fields and methods of the embedded type get promoted, becoming directly accessible as if they belonged to the outer type.
Struct embedding
type Animal struct {
Name string
}
func (a Animal) Greet() string {
return "ciao da " + a.Name
}
type Dog struct {
Animal // embedding — NO field name
Breed string
}
c := Dog{
Animal: Animal{Name: "Rex"},
Breed: "Pastore",
}
fmt.Println(c.Name) // "Rex" — promoted from Animal
fmt.Println(c.Greet()) // "ciao da Rex" — promoted method
fmt.Println(c.Breed) // own fieldThe embedded field is still accessible by the type's name:
c.Animal.Name works and is identical to c.Name. It is needed when
there are collisions.
Override (partial): method "shadowing"
If the outer type defines a method with the same name as the promoted one, its method "wins". From inside you can always reach the embedded one via the type's name:
func (c Dog) Greet() string {
return "BAU! " + c.Animal.Greet()
}It is not polymorphic inheritance: it is just lexical name resolution. For polymorphism you use interfaces (Module 6).
Multiple embedding
You can embed multiple types:
type Logger struct{ /*...*/ }
func (l Logger) Log(msg string) { /*...*/ }
type Timer struct{ /*...*/ }
func (t Timer) Now() time.Time { /*...*/ }
type Service struct {
Logger
Timer
Name string
}
s := Service{Name: "api"}
s.Log("start") // promoted from Logger
s.Now() // promoted from TimerIf two embedded types have a method with THE SAME name, direct access
becomes ambiguous and the compiler requires the explicit form
(s.Logger.Foo() or s.Timer.Foo()).
Interface embedding
Interfaces can also be "embedded" (see Module 6):
type ReadWriter interface {
io.Reader
io.Writer
}It is equivalent to the union of Reader's and Writer's methods.
When to use explicit composition (not embedding)
If you want a named field and no auto-promotion, use plain composition:
type Dog struct {
Anim Animal // NORMAL field, no promotion
Breed string
}
c.Anim.Name // you need the field nameEmbedding is a handy delegation tool, not a design choice strictly better than explicit composition: use it when you really want to promote the inner type's API.
Try it
Define Dog which embeds Animal (embedding, no field name) and has a Breed string field.
Show hint
Embedding: write the TYPE without giving it a field name.
Solution available after 3 attempts
Add a Greet() string method to Animal and call it directly on a Dog variable (method promotion).
Show hint
The method defined on Animal is promoted to Dog thanks to embedding.
Solution available after 3 attempts
Does Go support classical inheritance?
type B struct { /* extends A? */ }Recap
- No inheritance: you compose with embedding.
- Embedding = writing the TYPE without a field name in the struct.
- Fields and methods of the embedded type get promoted.
- Explicit access is always available:
outer.Inner.Field. - "Lexical" override: the outer type's method shadows the promoted one.
- Embedding ≠ is-a: for polymorphism use interfaces.