Skip to content

Embedding

Go has no extends, no base class, and no virtual dispatch. Instead it has embedding: you place one type inside another without a field name, and the outer type gains all of the inner type’s fields and methods as if they were its own.

type Named struct {
Name string
}
func (n Named) Describe() string { return "Name: " + n.Name }
type Employee struct {
Named // embedded — no field name
Department string
}

Now Employee has a Name field and a Describe() method promoted from Named:

e := Employee{Named: Named{Name: "Alice"}, Department: "Engineering"}
fmt.Println(e.Name) // promoted field
fmt.Println(e.Describe()) // promoted method

When you access e.Name, Go looks for a field or method named Name directly on Employee first. If none is found, it descends into each embedded type in order. This lookup is purely syntactic sugar — the long form e.Named.Name is always valid too.

You can override a promoted method by declaring a method with the same name on the outer type:

func (e Employee) Describe() string {
return fmt.Sprintf("%s in %s", e.Name, e.Department)
}

Now e.Describe() calls the outer method. The inner method is still reachable via e.Named.Describe().

Interfaces can also embed other interfaces:

type Stringer interface {
String() string
}
type Describer interface {
Stringer // embeds Stringer
Describe() string
}

Any type that satisfies Describer also satisfies Stringer automatically. The standard library uses this extensively: io.ReadWriter embeds io.Reader and io.Writer.

A critical distinction:

Inheritance (Java/C++)Embedding (Go)
Virtual dispatch: method call on outer type can dispatch to inner’s overrideNo virtual dispatch: calling the inner method always calls that method, regardless of what is on the outer type
instanceof / type hierarchyNo hierarchy — types are unrelated
Base class can reference derived class via virtual methodsEmbedded type has no knowledge of the outer type

Because there is no virtual dispatch, embedding does not allow polymorphism on the outer type. To get polymorphism, you still need an interface.

package main
import "fmt"
type Named struct {
Name string
}
func (n Named) Describe() string {
return "Name: " + n.Name
}
type Employee struct {
Named
Department string
}
// Interface embedding
type Stringer interface {
String() string
}
type Describer interface {
Stringer
Describe() string
}
func (e Employee) String() string {
return fmt.Sprintf("%s (Dept: %s)", e.Name, e.Department)
}
func print(d Describer) {
fmt.Println(d.String())
fmt.Println(d.Describe())
}
func main() {
e := Employee{
Named: Named{Name: "Alice"},
Department: "Engineering",
}
// promoted field and method
fmt.Println(e.Name)
fmt.Println(e.Describe())
print(e)
}
An `Employee` embeds `Named`. How do you access the promoted `Name` field?
You embed `Named` in `Employee` and also declare `func (e Employee) Describe() string`. Which method does `e.Describe()` call?
Interface `io.ReadWriter` embeds `io.Reader` and `io.Writer`. A type that satisfies `io.ReadWriter` automatically satisfies…
What is the main difference between Go embedding and Java inheritance?