Menu
CoddyTech

方法提升

本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 36 课,共 107 课。

就像结构体嵌入会将字段提升到外层结构体一样,Go 也会提升嵌入类型的方法。这意味着你可以直接在外层结构体上调用定义在嵌入结构体中的方法。

type Engine struct {
    Horsepower int
}

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

type Car struct {
    Model string
    Engine  // 嵌入字段
}

由于 Car 嵌入了 Engine,Start() 方法会自动提升:

func main() {
    c := Car{
        Model:  "Sedan",
        Engine: Engine{Horsepower: 200},
    }
    
    fmt.Println(c.Start())         // 引擎已启动
    fmt.Println(c.Engine.Start())  // 这样也可以
}

这种提升对接口有一个重要影响。如果嵌入类型满足某个接口,外层类型也会自动满足该接口:

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 通过 Engine 实现了 Starter 接口
}

Car 类型从未显式实现 Starter,但它满足该接口,因为它嵌入的 Engine 具有所需的方法。这就是 Go 如何在不使用传统继承的情况下复用行为。

challenge icon

挑战

简单

让我们构建一个音乐播放器系统,演示嵌入类型的方法如何提升到外部类型,以及组合如何由此实现接口满足。

你将把代码分成两个文件:

  • audio.go:创建音乐播放器的基本构件:
    • 一个 AudioPlayer 结构体,包含 Brand 字段(字符串),以及一个 Play() string 方法,该方法返回 [Brand] playing audio
    • 一个要求包含 Play() string 方法的 Playable 接口
    • 一个包含 Model 字段并嵌入 AudioPlayer 的 Smartphone 结构体
    • 一个包含 Model 字段并嵌入 AudioPlayer 的 Tablet 结构体
  • main.go:创建一个名为 StartPlayback 的函数,该函数接受任意 Playable,并返回调用 Play() 的结果。从输入中读取设备信息,创建一个 Smartphone 和一个 Tablet,并演示这两种类型如何通过嵌入的 AudioPlayer 满足 Playable 接口,尽管它们都没有显式实现自己的 Play() 方法。

将提供以下输入:

  • 第 1 行:Smartphone 型号名称
  • 第 2 行:Smartphone 音频品牌
  • 第 3 行:Tablet 型号名称
  • 第 4 行:Tablet 音频品牌

对于每个设备,先输出其型号,再输出将其传递给 StartPlayback 后得到的结果。

例如,给定 iPhone 15、Apple Audio、iPad Pro 和 Beats,你的输出应为:

iPhone 15
Apple Audio playing audio
iPad Pro
Beats playing audio

注意,Smartphone 和 Tablet 都可以传递给 StartPlayback,因为来自 AudioPlayer 的 Play() 方法会自动提升到每种外部类型,使它们满足 Playable 接口,无需添加任何代码。

亲手试试

package main

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

// TODO: 创建一个接受任意 Playable 的 StartPlayback 函数
// 并返回调用 Play() 的结果

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// 读取智能手机型号
	scanner.Scan()
	smartphoneModel := scanner.Text()
	
	// 读取智能手机的音频品牌
	scanner.Scan()
	smartphoneBrand := scanner.Text()
	
	// 读取平板电脑型号
	scanner.Scan()
	tabletModel := scanner.Text()
	
	// 读取平板电脑的音频品牌
	scanner.Scan()
	tabletBrand := scanner.Text()
	
	// TODO: 创建一个嵌入 AudioPlayer 的 Smartphone
	
	// TODO: 创建一个嵌入 AudioPlayer 的 Tablet
	
	// TODO: 输出智能手机型号,然后以智能手机为参数调用 StartPlayback
	
	// TODO: 输出平板电脑型号,然后以平板电脑为参数调用 StartPlayback
	
	_ = smartphoneModel
	_ = smartphoneBrand
	_ = tabletModel
	_ = tabletBrand
	fmt.Println("TODO: Complete the implementation")
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Go 在线编译器