Menu
Coddy logo textTech

Method Promotion

Part of the Object Oriented Programming section of Coddy's GO journey — lesson 36 of 107.

Just as struct embedding promotes fields to the outer struct, Go also promotes methods from embedded types. This means you can call methods defined on the embedded struct directly on the outer struct.

type Engine struct {
    Horsepower int
}

func (e Engine) Start() string {
    return "Engine started"
}

type Car struct {
    Model string
    Engine  // embedded
}

Since Car embeds Engine, the Start() method is automatically promoted:

func main() {
    c := Car{
        Model:  "Sedan",
        Engine: Engine{Horsepower: 200},
    }
    
    fmt.Println(c.Start())         // Engine started
    fmt.Println(c.Engine.Start())  // also works
}

This promotion has an important consequence for interfaces. If the embedded type satisfies an interface, the outer type automatically satisfies it too:

type Starter interface {
    Start() string
}

func Ignite(s Starter) {
    fmt.Println(s.Start())
}

func main() {
    c := Car{Model: "Sedan", Engine: Engine{Horsepower: 200}}
    Ignite(c)  // Car satisfies Starter through Engine
}

The Car type never explicitly implements Starter, yet it satisfies the interface because its embedded Engine has the required method. This is how Go achieves behavior reuse without traditional inheritance.

challenge icon

Challenge

Easy

Let's build a music player system that demonstrates how methods from embedded types get promoted to the outer type, and how this enables interface satisfaction through composition.

You'll organize your code across two files:

  • audio.go: Create the building blocks for your music player:
    • An AudioPlayer struct with a Brand field (string) and a Play() string method that returns [Brand] playing audio
    • A Playable interface requiring a Play() string method
    • A Smartphone struct with a Model field that embeds AudioPlayer
    • A Tablet struct with a Model field that embeds AudioPlayer
  • main.go: Create a function called StartPlayback that accepts any Playable and returns the result of calling Play(). Read device information from input, create both a Smartphone and a Tablet, and demonstrate that both satisfy the Playable interface through their embedded AudioPlayer—even though neither type explicitly implements Play() itself.

The following inputs will be provided:

  • Line 1: Smartphone model name
  • Line 2: Smartphone audio brand
  • Line 3: Tablet model name
  • Line 4: Tablet audio brand

For each device, print its model followed by the result of passing it to StartPlayback.

For example, given iPhone 15, Apple Audio, iPad Pro, and Beats, your output should be:

iPhone 15
Apple Audio playing audio
iPad Pro
Beats playing audio

Notice how both Smartphone and Tablet can be passed to StartPlayback because the Play() method from AudioPlayer is automatically promoted to each outer type, satisfying the Playable interface without any additional code.

Cheat sheet

When a struct embeds another type, methods from the embedded type are automatically promoted to the outer struct:

type Engine struct {
    Horsepower int
}

func (e Engine) Start() string {
    return "Engine started"
}

type Car struct {
    Model string
    Engine  // embedded
}

func main() {
    c := Car{
        Model:  "Sedan",
        Engine: Engine{Horsepower: 200},
    }
    
    fmt.Println(c.Start())         // Engine started
    fmt.Println(c.Engine.Start())  // also works
}

If an embedded type satisfies an interface, the outer type automatically satisfies it too:

type Starter interface {
    Start() string
}

func Ignite(s Starter) {
    fmt.Println(s.Start())
}

func main() {
    c := Car{Model: "Sedan", Engine: Engine{Horsepower: 200}}
    Ignite(c)  // Car satisfies Starter through Engine
}

This enables behavior reuse through composition without traditional inheritance.

Try it yourself

package main

import (
	"bufio"
	"fmt"
	"os"
)

// TODO: Create a StartPlayback function that accepts any Playable
// and returns the result of calling Play()

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Read smartphone model
	scanner.Scan()
	smartphoneModel := scanner.Text()
	
	// Read smartphone audio brand
	scanner.Scan()
	smartphoneBrand := scanner.Text()
	
	// Read tablet model
	scanner.Scan()
	tabletModel := scanner.Text()
	
	// Read tablet audio brand
	scanner.Scan()
	tabletBrand := scanner.Text()
	
	// TODO: Create a Smartphone with the embedded AudioPlayer
	
	// TODO: Create a Tablet with the embedded AudioPlayer
	
	// TODO: Print smartphone model, then call StartPlayback with the smartphone
	
	// TODO: Print tablet model, then call StartPlayback with the tablet
	
	_ = smartphoneModel
	_ = smartphoneBrand
	_ = tabletModel
	_ = tabletBrand
	fmt.Println("TODO: Complete the implementation")
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming