学习 Go 结构体方法时,必须彻底分清类型名和变量名,否则容易报错或理解错误。
一、核心概念
1. 类型名
由 type 定义的名字,代表「种类、模板」,不是具体对象。
示例:
go
type Stu struct {
Age int
}
Stu是类型名,地位等同于int、string。- 特点:
- 只代表"这一类数据",没有内存、没有具体值
- 不能直接使用
.字段,例如Stu.Age会报错
2. 变量名
根据类型创建的实例,占用内存,存储真实数据。
示例:
go
var s Stu
s.Age = 18
s是变量名(具体对象)- 特点:
- 可以访问字段
.Age - 是实实在在的对象,存储实际数据
- 可以访问字段
3. 极简口诀
类型 = 模板
变量 = 实物
只能用实物访问字段,类型不能访问字段
二、为什么 (stu) getAge() 必报错
错误示例:
go
func (stu) getAge() int {
return stu.Age
}
报错原因:
(stu)只有类型名,没有接收者变量名- 方法体内的
stu不是实例变量 - 类型本身没有字段,无法访问
stu.Age
正确写法:
go
func (s Stu) getAge() int {
return s.Age
}
s:接收者变量Stu:接收者类型
三、Go 方法接收者固定语法
go
func (变量名 类型) 方法名() 返回值类型 {}
- 变量名:代表当前调用方法的对象(实例)
- 类型:结构体类型
特殊补充
只写类型、不写变量名,代码能编译,但无法访问任何字段:
go
func (Stu) hello() string {
return "hi"
}
场景:纯工具方法、不操作对象字段时可用,业务开发几乎不用。
四、值接收者 vs 指针接收者
1. 值接收者 (s Stu)
- 传副本,修改不影响原对象
- 值和指针对象都可以调用
示例:
go
func (s Stu) SetAge(age int) {
s.Age = age
}
调用:
go
stu := Stu{18}
stu.SetAge(20)
fmt.Println(stu.Age) // 18,原对象未修改
2. 指针接收者 (s *Stu)
- 传原对象地址,修改会影响原对象
- 调用时 Go 会自动取地址:
s.setAge()等价于(&s).setAge()
示例:
go
func (s *Stu) SetAge(age int) {
s.Age = age
}
stu := Stu{18}
stu.SetAge(20)
fmt.Println(stu.Age) // 20,原对象被修改
五、终极总结
Stu是类型名:模板、种类,不能访问字段s是变量名:实例对象,可以访问字段- 方法接收者必须
(变量名 类型),缺一不可 - 无变量名的接收者,无法操作结构体字段
- 值接收者修改的是副本,指针接收者修改的是原对象
stu.GetAge()本质类似GetAge(stu),接收者变量代表当前调用对象
六、一句话记忆法
类型 = 图纸
变量 = 房子
字段属于房子,不属于图纸
接收者变量代表当前这套房子
接收者类型代表这种房子的设计图