Go语言第六章(结构体)

结构体

结构体是 Go 中的一种复合数据类型,用于将多个不同类型的数据组合在一起,形成一个新的数据类型。日常开发里,用结构体描述「一个人」「一辆车」「一条订单」这类对象非常常见。

定义结构体

type + struct 定义一种新类型:

go 复制代码
type 结构体名 struct {
	字段名 类型
	字段名 类型
	字段名 类型
}

实操:定义一个 Person,包含姓名、年龄和爱好:

go 复制代码
package main

import "fmt"

type Person struct {
	Name  string
	Age   int
	Hobby []string
}

func main() {
	person := Person{
		Name:  "John",
		Age:   20,
		Hobby: []string{"reading", "swimming"},
	}
	fmt.Println(person)
}

Person{...} 是字面量初始化:按字段名赋值,顺序可以随便写,没写的字段会是该类型的零值。

字段读写

通过 . 访问和修改字段:

go 复制代码
package main

import "fmt"

type Person struct {
	Name string
	Age  int
}

func main() {
	p := Person{Name: "张三", Age: 18}
	fmt.Println(p.Name) // 张三

	p.Age = 20
	fmt.Println(p.Age) // 20
}

几种初始化方式

go 复制代码
package main

import "fmt"

type Person struct {
	Name string
	Age  int
}

func main() {
	// 1. 按字段名初始化(推荐,可读性好)
	p1 := Person{Name: "张三", Age: 18}

	// 2. 按字段顺序初始化(字段多时容易写错,不推荐)
	p2 := Person{"李四", 20}

	// 3. 只给部分字段赋值,其余为零值
	p3 := Person{Name: "王五"} // Age 为 0

	// 4. 先声明再赋值(全部为零值)
	var p4 Person
	p4.Name = "赵六"
	p4.Age = 22

	// 5. 取地址,得到 *Person
	p5 := &Person{Name: "钱七", Age: 25}

	fmt.Println(p1, p2, p3, p4, p5)
}

指针形式 p5 访问字段时,Go 会自动解引用,写 p5.Name 即可,不必写 (*p5).Name

结构体嵌套

一个结构体里可以包含另一个结构体类型的字段,用来组合更复杂的数据:

go 复制代码
package main

import "fmt"

type Car struct {
	Brand string
	Model string
	Year  int
}

type Person struct {
	Name  string
	Age   int
	Hobby []string
	Car   Car
}

func main() {
	person := Person{
		Name:  "John",
		Age:   20,
		Hobby: []string{"reading", "swimming"},
		Car: Car{
			Brand: "Toyota",
			Model: "Camry",
			Year:  2020,
		},
	}
	fmt.Println(person)
	fmt.Println(person.Car.Brand) // Toyota
}

这里 Car 是命名字段,访问时要写 person.Car.Brand

结构体嵌入

如果字段类型前不写字段名,就是嵌入(匿名字段)。嵌入后,被嵌入类型的字段和方法会「提升」到外层,可以直接用:

go 复制代码
package main

import "fmt"

type Car struct {
	Brand string
	Model string
	Year  int
}

type Person struct {
	Name string
	Age  int
	Car        // 嵌入:没有字段名,只有类型
}

func main() {
	person := Person{
		Name: "John",
		Age:  20,
		Car: Car{
			Brand: "Toyota",
			Model: "Camry",
			Year:  2020,
		},
	}

	// 提升后可以直接访问
	fmt.Println(person.Brand) // Toyota
	// 也可以走完整路径
	fmt.Println(person.Car.Model) // Camry
}

嵌套是「有名字的组合」;嵌入是「把另一个类型的能力提升过来」。两者都能组合数据,嵌入更适合表达「is-a / 带有某种能力」的关系。

方法

方法是「绑在某个类型上的函数」。接收者写在 func 和函数名之间:

go 复制代码
func (接收者 类型) 方法名(参数列表) 返回值 {
	// ...
}

值接收者

go 复制代码
package main

import "fmt"

type Person struct {
	Name string
	Age  int
}

func (p Person) Introduce() {
	fmt.Printf("我叫 %s,今年 %d 岁\n", p.Name, p.Age)
}

func main() {
	p := Person{Name: "张三", Age: 18}
	p.Introduce() // 我叫 张三,今年 18 岁
}

值接收者拿到的是副本,方法里改字段不会影响原变量。

指针接收者

需要修改结构体本身时,用指针接收者:

go 复制代码
package main

import "fmt"

type Person struct {
	Name string
	Age  int
}

func (p *Person) GrowUp() {
	p.Age++
}

func (p Person) Introduce() {
	fmt.Printf("我叫 %s,今年 %d 岁\n", p.Name, p.Age)
}

func main() {
	p := Person{Name: "张三", Age: 18}
	p.GrowUp()
	p.Introduce() // 我叫 张三,今年 19 岁
}

调用时写 p.GrowUp() 即可,Go 会自动取地址。习惯上:要改状态用指针接收者;只读、小结构体可以用值接收者。同一类型的方法最好统一风格,不要有的用值、有的用指针混着来。

匿名结构体

不提前 type 定义,直接在用的地方写 struct { ... },适合只临时用一次的场景:

go 复制代码
package main

import "fmt"

func main() {
	person := struct {
		Name string
		Age  int
	}{
		Name: "John",
		Age:  20,
	}
	fmt.Println(person.Name, person.Age)
}

配置、单次返回、测试数据等「用完就丢」的结构,用匿名结构体很方便。若多处复用,还是起个名字更清晰。

泛型结构体

Go 1.18 起,结构体也可以带类型参数:

go 复制代码
package main

import "fmt"

type Person[T string | int] struct {
	Name  string
	Age   int
	Phone T
}

func main() {
	person := Person[string]{
		Name:  "John",
		Age:   20,
		Phone: "010-10086",
	}
	person2 := Person[int]{
		Name:  "John",
		Age:   20,
		Phone: 10086,
	}
	fmt.Println(person)
	fmt.Println(person2)
}

Person[T string | int] 表示 Phone 只能是 stringint。创建时要写明具体类型,如 Person[string]Person[int]

作业

  1. 定义与初始化 :定义 Book 结构体(书名、作者、页数),用至少两种方式创建实例并打印。
  2. 嵌套 :定义 Address(城市、街道)和 User(姓名、地址),创建用户并打印「姓名住在城市的街道」。
  3. 嵌入 :把上一题的 Address 改成嵌入到 User,用提升后的字段直接打印城市。
  4. 方法 :给 Book 写值接收者方法 Info() 打印信息;再写指针接收者方法 AddPages(n int) 增加页数,调用后验证页数有变。
  5. 综合 :定义 Rectangle(宽、高),写方法 Area()Perimeter() 返回面积和周长,并在 main 里验证。
相关推荐
属于自己的天空1 小时前
每天省去重复 Prompt:我用 Skills 把 CRUD 生成标准化了
前端·后端
卡卡罗特AI1 小时前
GitHub怎么用?零基础,保姆级使用教程-上!建议收藏
前端·后端·github
老孙讲技术1 小时前
家长端看幼儿园 live:云直播 HLS 嵌入公众号 / H5 的合规要点
后端·物联网
xingren1 小时前
CT(DCM)外轮廓实时识别 → 逐层拼3D → 导出STL到Blender
后端
foggyprojects1 小时前
同一条销售查询,两个用户为什么看到不同结果?
后端
梅头脑1 小时前
Java镜像1.2GB→200MB,K8s Pod OOMKilled 137半夜报警——容器化的真实踩坑记录
后端
netCode2 小时前
IDEA使用 Alibaba Cloud Toolkit
后端
foggyprojects2 小时前
业务要给发票明细加 8 个筛选条件,我没有再写 8 个查询接口
后端
爱勇宝2 小时前
人到了一定年纪,才会看懂这些人性真相
前端·后端