Go值接收者与指针接收者
方法接收者类型决定方法能否修改原值,影响接口满足规则。
值接收者
定义与行为
Go
type User struct {
Name string
Age int
}
func (u User) GetName() string {
return u.Name // 读取,OK
}
func (u User) SetAge(age int) {
u.Age = age // 修改副本,无效!
}
u := User{Name: "Tom", Age: 25}
u.SetAge(30)
fmt.Println(u.Age) // 25(未改变)
值接收者操作副本,修改不影响原值。
特点
- 方法内操作的是副本
- 读取有效,修改无效
- 方法调用不修改原值
- 适合只读操作
指针接收者
定义与行为
Go
func (u *User) SetAge(age int) {
u.Age = age // 修改原值,有效!
}
func (u *User) SetName(name string) {
u.Name = name // 修改原值,有效!
}
u := User{Name: "Tom", Age: 25}
u.SetAge(30) // 自动转换为(&u).SetAge(30)
fmt.Println(u.Age) // 30(已改变)
指针接收者操作原值,修改有效。
特点
- 方法内操作的是原值
- 读取和修改都有效
- 可以修改接收者状态
- 适合需要修改的场景
自动转换规则
值调用方法
Go
u := User{Name: "Tom"}
// 值调用值接收者方法:直接调用
u.GetName() // OK
// 值调用指针接收者方法:自动转换为指针
u.SetAge(30) // 自动转换为(&u).SetAge(30)
指针调用方法
Go
u := &User{Name: "Tom"}
// 指针调用指针接收者方法:直接调用
u.SetAge(30) // OK
// 指针调用值接收者方法:自动解引用
u.GetName() // 自动转换为(*u).GetName()
Go自动处理值/指针调用的转换,但接口赋值不自动转换。
选择原则
使用指针接收者的场景
Go
// 1. 需要修改接收者
func (u *User) SetAge(age int) {
u.Age = age
}
// 2. 接收者较大(避免复制)
type BigData struct {
Data [10000]byte
}
func (b *BigData) Process() {
// 指针避免大结构体复制
}
// 3. 保持一致性(其他方法用指针)
func (u *User) Method1() {}
func (u *User) Method2() {} // 统一用指针
使用值接收者的场景
Go
// 1. 只读取不修改
func (u User) GetName() string {
return u.Name
}
// 2. 接收者较小(复制成本低)
type Point struct {
X, Y int
}
func (p Point) Distance() float64 {
// 小结构体,复制成本低
}
// 3. 不需要修改且值语义清晰
func (t Time) Format() string {
// 时间格式化,不需要修改
}
接收者选择对比
| 场景 | 推荐 | 原因 |
|---|---|---|
| 需修改接收者 | 指针 | 唯一能修改的方式 |
| 大结构体 | 指针 | 避免复制开销 |
| 小结构体只读 | 值 | 复制成本低 |
| 保持一致性 | 统一 | 避免混用混乱 |
| 基础类型 | 值 | 本质是值语义 |
接口满足差异
值接收者
Go
type Reader interface {
Read() string
}
type MyReader struct{}
func (m MyReader) Read() string { return "data" }
// 值和指针都满足接口
var r Reader = MyReader{} // ✓
var r Reader = &MyReader{} // ✓
指针接收者
Go
type Writer interface {
Write(data string)
}
type MyWriter struct{}
func (m *MyWriter) Write(data string) {}
// 只有指针满足接口
var w Writer = MyWriter{} // ✗ 编译错误
var w Writer = &MyWriter{} // ✓
最佳实践示例
Go
type Counter struct {
value int
}
// 值接收者:只读操作
func (c Counter) Value() int {
return c.value
}
// 指针接收者:修改操作
func (c *Counter) Increment() {
c.value++
}
func (c *Counter) Reset() {
c.value = 0
}
要点总结
- 值接收者操作副本,修改无效
- 指针接收者操作原值,修改有效
- 方法调用时Go自动转换值/指针
- 接口赋值不自动转换指针
- 指针接收者:值类型不满足接口
- 值接收者:值和指针都满足接口
- 选择原则:需修改用指针,只读用值
- 大结构体推荐指针,避免复制开销
- 同一类型方法保持接收者一致性
📝 发现内容有误?点击此处直接编辑