Go Structs Embedded Fields
External
Internal
Overview
Embedding, Interfaces and Polymorphism
TO DEPLETE
Field embedding is used to share state in inheritance. When only the type but not the name of a filed is declared, the name of the field is implicitly the unqualified name of the type, and the field is called an embedded field or an anonymous field:
type SomeInterface interface {
...
}
type SomeOtherStruct struct {
s string
}
type Item struct {
SomeInterface
SomeOtherStruct
int
}
Embedded fields, unlike named fields, model an is-a relationship.
An embedded field can be a type name T
, a pointer to a non-interface type name (*T
), and T
itself may not be a pointer type. As mentioned above, the unqualified type name becomes the implicit field name. The previous example declares three embedded fields, one of type int
, one of type SomeInterface
, which is an interface declared somewhere in the package and the third of type SomeOtherStruct
, which is a struct type declared somewhere else in the package. The type renders as:
{SomeInterface:<nil> SomeOtherStruct:{s:} int:0}
Note that if an embedded field is specified using the package and the type name (the qualified type name), the implicit field name is the unqualified type name.
Embedded Field Promotion
The embedded field's type identifiers are promoted to the embedding type, so they can be accessed as it would belong to the embedding type. Promotion also applies to methods associated with the embedded type. A method associated with the embedded type works with the embedding type. This strengthens the point that embedded fields model an "is-a" relationship, and elevates the field embedding mechanism to a sort of inheritance mechanism. Type embedding is the Go's "extends". It allows types to extend, and it changes their behavior.
Field promotion:
type Animal struct {
name string
}
type Dog struct {
Animal
}
dog := &Dog{Animal{"Fido"}}
...
fmt.Printf("name: %s\n", dog.name) // the "name" field is promoted into the Dog structure
Method promotion, for the same structs Animal
and Dog
declared above:
func (a *Animal) Move() {
fmt.Printf("%s moves\n", a.name)
}
...
dog.Move() // Displays "Fido moves"
In the example above, the invocations dog.Move()
and dog.Animal.Move()
are equivalent.
If an interface is implemented by the embedded type, it is promoted to the embedding type.
Also see:
Promoted Fields
TODO: https://go.dev/ref/spec#Struct_types
Embedded Field Identity
The embedded field always exists in and of itself. It never loses its identity and it can be always accessed directly:
dog.Animal.name
Overriding Embedded Fields
The embedding type can override the embedding field elements, and reuse the identifiers, associate them with other types, etc. Both fields and methods can be overridden. This behavior provides polymorphism.
type Animal struct {
name string
}
func (a *Animal) Move() {
fmt.Printf("%s moves\n", a.name)
}
type Dog struct {
Animal
}
func (d *Dog) Move() {
fmt.Printf("%s jumps\n", d.name)
}
...
dog := &Dog{Animal{"Fido"}}
dog.Move() // will print "Fido jumps"
When the embedding type does not want to implement an embedded type interface, it can override at least one of the method of the embedded type method set. Example required.
Retire "Go_Inheritance_and_Polymorphism"
Retire: