结构体嵌入基础
本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 35 课,共 107 课。
结构体嵌入是 Go 将一种结构体类型包含在另一种结构体中的机制。你无需使用字段名,只需指定类型,Go 就会将嵌入结构体的字段提升到外层结构体中。
这是基本语法:
type Address struct {
City string
Country string
}
type Person struct {
Name string
Address // 嵌入字段,没有字段名
}
将 Address 嵌入 Person 后,你可以直接通过外层结构体访问嵌入的字段:
func main() {
p := Person{
Name: "Alice",
Address: Address{
City: "London",
Country: "UK",
},
}
fmt.Println(p.City) // London:直接访问
fmt.Println(p.Country) // UK:直接访问
fmt.Println(p.Address.City) // 这样也可以
}
注意,p.City 无需显式写出 p.Address.City 就能正常使用。Go 会自动提升嵌入结构体的字段,使你可以像访问外层结构体的字段一样访问它们。需要时,你仍然可以通过嵌入类型的名称访问这些字段。
这与常规的嵌套结构体不同,访问内部字段时,你必须始终使用字段名。嵌入可以创建更扁平、更方便的 API,同时保留相关数据的逻辑分组。
挑战
简单让我们构建一个使用结构体嵌入的车辆登记系统,创建清晰、有条理的数据模型。你会将联系信息与车辆详情分开,同时通过嵌入实现对所有字段的直接访问。
你会将代码分成两个文件:
vehicle.go:创建两个通过嵌入协同工作的结构体:- 一个包含
OwnerName和Phone字段(两者都是字符串)的ContactInfo结构体 - 一个嵌入
ContactInfo(不带字段名),并添加自己的Make、Model和Year字段的Vehicle结构体
Vehicle创建一个名为Summary的方法,返回包含所有车辆和联系信息的格式化字符串。- 一个包含
main.go:从输入中读取车辆和车主详情,创建一个嵌入了ContactInfo的Vehicle实例,并演示如何直接访问嵌入字段,以及通过嵌入类型名访问这些字段。最后打印车辆摘要。
输入将按以下格式提供:
- 第 1 行:车主姓名
- 第 2 行:电话号码
- 第 3 行:车辆品牌
- 第 4 行:车辆型号
- 第 5 行:年份(整数)
你的 Summary 方法应返回以下格式的字符串:
[Year] [Make] [Model] - Owner: [OwnerName], Phone: [Phone]在主文件中,通过打印以下内容来演示字段提升:
- 直接通过 Vehicle 访问的车主姓名(例如,
v.OwnerName) - 通过嵌入类型访问的电话号码(例如,
v.ContactInfo.Phone) - 通过你的
Summary方法生成的完整摘要
例如,给定 Alice Smith、555-1234、Toyota、Camry 和 2023,你的输出应为:
Alice Smith
555-1234
2023 Toyota Camry - Owner: Alice Smith, Phone: 555-1234注意,这两种访问方式都可行:通过字段提升直接访问,以及通过嵌入类型名显式访问。这让你可以灵活地处理嵌入的数据。
亲手试试
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// 读取车主姓名
ownerName, _ := reader.ReadString('\n')
ownerName = strings.TrimSpace(ownerName)
// 读取电话号码
phone, _ := reader.ReadString('\n')
phone = strings.TrimSpace(phone)
// 读取车辆品牌
make, _ := reader.ReadString('\n')
make = strings.TrimSpace(make)
// 读取车辆型号
model, _ := reader.ReadString('\n')
model = strings.TrimSpace(model)
// 读取年份
yearStr, _ := reader.ReadString('\n')
yearStr = strings.TrimSpace(yearStr)
year, _ := strconv.Atoi(yearStr)
// TODO: 创建一个嵌入 ContactInfo 的 Vehicle 实例
// 使用上面从输入中读取的值
// TODO: 打印直接通过 Vehicle 访问的车主姓名(字段提升)
// 示例:fmt.Println(v.OwnerName)
// TODO: 打印通过嵌入类型名称访问的电话号码
// 示例:fmt.Println(v.ContactInfo.Phone)
// TODO: 使用 Summary 方法打印完整摘要
// 示例:fmt.Println(v.Summary())
// 实现答案后,删除这些代码行。
_ = ownerName
_ = phone
_ = make
_ = model
_ = year
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Go 在线编译器