结构体方法 #
一、方法概述 #
方法是带有接收者的函数,可以为类型定义行为。
二、方法定义 #
2.1 基本语法 #
go
func (接收者 类型) 方法名(参数) 返回值 {
// 方法体
}
2.2 示例 #
go
type Person struct {
Name string
Age int
}
func (p Person) SayHello() {
fmt.Printf("Hello, I'm %s\n", p.Name)
}
func (p Person) GetAge() int {
return p.Age
}
p := Person{Name: "Alice", Age: 25}
p.SayHello() // Hello, I'm Alice
fmt.Println(p.GetAge()) // 25
三、值接收者 #
3.1 定义 #
go
func (p Person) SetName(name string) {
p.Name = name // 修改的是副本
}
p := Person{Name: "Alice"}
p.SetName("Bob")
fmt.Println(p.Name) // Alice(未改变)
3.2 特点 #
- 方法内修改不影响原值
- 适合不修改状态的方法
3.3 适用场景 #
go
func (p Person) GetName() string {
return p.Name
}
func (p Person) IsAdult() bool {
return p.Age >= 18
}
四、指针接收者 #
4.1 定义 #
go
func (p *Person) SetName(name string) {
p.Name = name // 修改原值
}
p := &Person{Name: "Alice"}
p.SetName("Bob")
fmt.Println(p.Name) // Bob
4.2 自动转换 #
Go会自动处理值和指针的转换:
go
p := Person{Name: "Alice"}
p.SetName("Bob") // 自动转换为 (&p).SetName("Bob")
p2 := &Person{Name: "Alice"}
p2.GetName() // 自动转换为 (*p2).GetName()
4.3 适用场景 #
- 需要修改接收者
- 结构体较大,避免复制
- 保持一致性
go
func (p *Person) Birthday() {
p.Age++
}
func (p *Person) SetAddress(addr string) {
p.Address = addr
}
五、方法集 #
5.1 规则 #
| 接收者类型 | 值调用 | 指针调用 |
|---|---|---|
| 值接收者 | ✓ | ✓ |
| 指针接收者 | ✓(自动转换) | ✓ |
5.2 示例 #
go
type Counter struct {
value int
}
func (c Counter) Value() int {
return c.value
}
func (c *Counter) Increment() {
c.value++
}
func main() {
c := Counter{value: 0}
c.Increment() // 自动转换
fmt.Println(c.Value()) // 1
p := &Counter{value: 0}
p.Increment()
fmt.Println(p.Value()) // 1
}
5.3 接口实现 #
接口实现时要注意方法集:
go
type Incrementer interface {
Increment()
}
type Counter struct {
value int
}
func (c *Counter) Increment() {
c.value++
}
func main() {
var i Incrementer = &Counter{} // 正确
// var i Incrementer = Counter{} // 错误:Counter没有实现Increment
}
六、方法继承 #
6.1 匿名嵌入 #
go
type Animal struct {
Name string
}
func (a Animal) Speak() {
fmt.Println("Some sound")
}
type Dog struct {
Animal
}
func main() {
d := Dog{Animal{Name: "Buddy"}}
d.Speak() // Some sound
}
6.2 方法重写 #
go
func (d Dog) Speak() {
fmt.Println("Woof!")
}
func main() {
d := Dog{Animal{Name: "Buddy"}}
d.Speak() // Woof!
d.Animal.Speak() // Some sound
}
七、方法值与表达式 #
7.1 方法值 #
go
p := Person{Name: "Alice"}
hello := p.SayHello // 方法值
hello() // Hello, I'm Alice
7.2 方法表达式 #
go
hello := Person.SayHello // 方法表达式
hello(p) // Hello, I'm Alice
setName := (*Person).SetName
setName(&p, "Bob")
7.3 作为参数 #
go
func process(p Person, f func(Person) string) {
fmt.Println(f(p))
}
p := Person{Name: "Alice"}
process(p, Person.GetName)
八、实际应用 #
8.1 链式调用 #
go
type Builder struct {
value strings.Builder
}
func (b *Builder) Write(s string) *Builder {
b.value.WriteString(s)
return b
}
func (b *Builder) WriteLine(s string) *Builder {
b.value.WriteString(s)
b.value.WriteString("\n")
return b
}
func (b *Builder) String() string {
return b.value.String()
}
func main() {
b := &Builder{}
result := b.Write("Hello").Write(" ").WriteLine("World").String()
fmt.Println(result)
}
8.2 验证方法 #
go
type User struct {
Name string
Email string
Age int
}
func (u User) Validate() error {
if u.Name == "" {
return errors.New("name is required")
}
if !strings.Contains(u.Email, "@") {
return errors.New("invalid email")
}
if u.Age < 0 {
return errors.New("invalid age")
}
return nil
}
8.3 状态管理 #
go
type Counter struct {
value int
mu sync.Mutex
}
func (c *Counter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.value++
}
func (c *Counter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.value
}
九、最佳实践 #
9.1 选择接收者类型 #
- 需要修改:指针接收者
- 结构体大:指针接收者
- 只读操作:值接收者
- 一致性:整个类型统一使用一种
9.2 方法命名 #
go
// 好
func (p *Person) SetName(name string) {}
func (p Person) GetName() string {}
func (p Person) IsAdult() bool {}
// 不好
func (p *Person) set_name(name string) {}
9.3 避免大结构体复制 #
go
type LargeStruct struct {
data [10000]int
}
// 好:指针接收者
func (l *LargeStruct) Process() {}
// 不好:值接收者,复制开销大
func (l LargeStruct) Process() {}
十、总结 #
方法要点:
| 特性 | 说明 |
|---|---|
| 值接收者 | 操作副本 |
| 指针接收者 | 操作原值 |
| 自动转换 | Go自动处理值/指针转换 |
| 方法集 | 影响接口实现 |
关键点:
- 接收者类型:根据是否需要修改选择
- 一致性:同一类型的所有方法使用相同接收者类型
- 方法值:方法可以赋值给变量
- 方法继承:匿名嵌入继承方法
- 接口实现:注意方法集规则
准备好学习结构体嵌套了吗?让我们进入下一章!
最后更新:2026-03-26