Go 入门(06):方法——值接收者 vs 指针接收者
更新时间:2026-09-01。本文是
languages/go/beginner/入门层第 6 篇,接 指针。Go 没有 class,但有方法——方法就是在函数前面加一个"接收者"参数。接收者是把结构体和方法绑定在一起的方式。
本文要回答的问题
- Go 的方法和 C 的成员函数、Java 的实例方法有什么区别?
- 值接收者和指针接收者有什么区别?
- 什么时候用值接收者,什么时候用指针接收者?
- 方法能加在哪些类型上?能加在 int 上吗?
一、方法语法
Go 的方法定义:在 func 和函数名之间加一个接收者。
go
type User struct {
Name string
Age int
}
// 值接收者:方法收到的是 User 的副本
func (u User) Greet() string {
return "Hello, " + u.Name
}
// 指针接收者:方法收到的是 *User,可以修改原结构体
func (u *User) Birthday() {
u.Age++
}调用:
go
u := User{Name: "Alice", Age: 25}
fmt.Println(u.Greet()) // Hello, Alice
u.Birthday() // 修改 Age
fmt.Println(u.Age) // 26接收者写在括号里,(u User) 表示方法是 User 类型的方法,(u *User) 表示方法是 *User 指针类型的方法。
二、值接收者 vs 指针接收者
值接收者:方法收到的是结构体的副本,修改不影响原变量:
go
func (u User) SetName(name string) {
u.Name = name // 改的是副本,外面没变
}
u := User{Name: "Alice"}
u.SetName("Bob")
fmt.Println(u.Name) // Alice,没变指针接收者:方法收到的是结构体的指针,可以修改原变量:
go
func (u *User) SetName(name string) {
u.Name = name // 通过指针修改原结构体
}
u := User{Name: "Alice"}
u.SetName("Bob")
fmt.Println(u.Name) // Bob,变了| 对比 | 值接收者 | 指针接收者 |
|---|---|---|
| 接收者类型 | (u User) | (u *User) |
| 能否修改原变量 | 不能,改的是副本 | 能,改的是指针指向的 |
| 拷贝成本 | 拷贝整个结构体 | 只拷贝 8 字节指针 |
| 什么时候用 | 不修改、小结构体 | 要修改、大结构体 |
三、选择原则
工程上常用的判断原则:
用指针接收者的情况:
- 方法需要修改接收者的字段
- 结构体很大(比如有几百字节),不想每次拷贝
- 结构体里有
sync.Mutex等不可拷贝的字段——必须用指针
用值接收者的情况:
- 方法不修改接收者
- 结构体很小(几个 int、bool)
- 遵循一致性原则:如果一个类型有多个方法,尽量统一用同样的接收者风格
官方建议:拿不准就用指针接收者。
四、方法调用时的自动转换
Go 在调用方法时,会自动处理指针和值的转换:
go
u := User{Name: "Alice"}
p := &u
// 指针接收者的方法,可以用值调用
u.Birthday() // 等价 (&u).Birthday(),Go 自动取地址
p.Birthday() // 指针调用,正常
// 值接收者的方法,可以用指针调用
fmt.Println(p.Greet()) // 等价 (*p).Greet(),Go 自动解引用底层是:Go 在调用时自动帮你做了 &u 或 *p 的转换。但要注意:只有变量可以取地址,字面量不行:
go
User{Name: "Alice"}.Birthday() // 编译错误!不能对字面量取地址五、方法能加在哪些类型上?
方法可以加在任何自定义类型上,不只是结构体:
go
type MyInt int
func (n MyInt) IsPositive() bool {
return n > 0
}
var n MyInt = 5
fmt.Println(n.IsPositive()) // true但不能为内置类型或别的包的类型加方法:
go
func (n int) Foo() {} // 编译错误:不能为内置类型 int 加方法,int 不是"当前包内的类型"六、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
| 值接收者想改字段 | 改了外面没变化 | 换成指针接收者 |
| 方法里接收者赋值 nil | 外面没影响,值是副本 | 指针接收者才能改 |
| 大结构体用值接收者 | 每次调用拷贝大块内存 | 换成指针接收者 |
| 不一致的接收者风格 | 同一类型混用值/指针 | 保持统一,拿不准用指针 |
相关与延伸
下一篇:接口 interface——Go 的隐式实现;方法接收者选择影响逃逸分析,见 GC 与逃逸分析;Go 用方法+接口实现面向对象,和 C++ 的虚函数对比见进阶层。
一句话总结
Go 方法:func (接收者) 方法名() 把函数绑定到类型;值接收者不修改原结构体,指针接收者可以修改;大结构体或要修改时用指针接收者,小结构体且不修改时用值接收者;调用时 Go 自动处理指针和值的转换,但字面量不能取地址。