8.2 Go Struct 方法学习笔记
1. 方法的定义 --- 值接收者
方法就是带"接收者"的函数。接收者写在函数名前面,用 (r Type) 的格式:
go
type Rectangle struct {
Width float64
Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
调用方式:
go
rect := Rectangle{Width: 5, Height: 3}
fmt.Printf("Area: %.2f\n", rect.Area()) // 15.00
fmt.Printf("Perimeter: %.2f\n", rect.Perimeter()) // 16.00
值接收者 func (r Rectangle):方法操作的是结构体的拷贝,不会修改原值。适合只读取数据、不修改数据的方法。
2. 不同类型可以有同名方法
Rectangle 和 Circle 都有 Area() 方法,但它们是不同类型的接收者,不会冲突:
go
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
调用时,Go 根据调用对象的类型自动选择对应的方法:
go
rect := Rectangle{Width: 5, Height: 3}
circle := Circle{Radius: 4}
rect.Area() // 调用 Rectangle 的 Area → 15.00
circle.Area() // 调用 Circle 的 Area → 50.27
Go 没有类继承,但不同类型可以有同名方法,这是实现接口的基础。
3. 指针接收者 --- 修改原值
当方法需要修改结构体本身时,必须使用指针接收者:
go
type BankAccount struct {
AccountNumber string
Balance float64
}
func (ba *BankAccount) Deposit(amount float64) {
ba.Balance += amount // 直接修改原值
}
func (ba *BankAccount) Withdraw(amount float64) bool {
if ba.Balance >= amount {
ba.Balance -= amount // 直接修改原值
return true
}
return false
}
调用后原值被修改:
go
account := BankAccount{AccountNumber: "12345", Balance: 1000.0}
account.Deposit(250)
// Balance: 1000 + 250 = 1250.00
account.Withdraw(500) // 成功,返回 true
// Balance: 1250 - 500 = 750.00
account.Withdraw(2000) // 失败,余额不足,返回 false
// Balance: 750.00(不变)
执行结果:
yaml
Initial account: Account 12345: $1000.00
After deposit: Balance = 1250.00
Withdrawal successful
Cannot withdraw $2000 - insufficient funds
Final account: Account 12345: $750.00
4. 值接收者 vs 指针接收者
值接收者 func (ba BankAccount) |
指针接收者 func (ba *BankAccount) |
|
|---|---|---|
| 操作对象 | 结构体的拷贝 | 结构体的原值 |
| 能否修改原值 | 不能 | 能 |
| 适用场景 | 只读方法(Get、计算) | 需要修改的方法(Set、Deposit、Withdraw) |
go
// 值接收者 --- 只读取,不修改
func (ba BankAccount) GetBalance() float64 {
return ba.Balance
}
// 指针接收者 --- 需要修改原值
func (ba *BankAccount) Deposit(amount float64) {
ba.Balance += amount
}
最佳实践:如果一个类型有任何一个方法用了指针接收者,那么所有方法都应该用指针接收者,保持一致性。
5. String() 方法 --- 实现 fmt.Stringer 接口
定义 String() 方法可以自定义结构体的打印格式:
go
func (ba BankAccount) String() string {
return fmt.Sprintf("Account %s: $%.2f", ba.AccountNumber, ba.Balance)
}
没有 String 方法时,fmt.Println(account) 输出:
css
{AccountNumber:12345 Balance:1000}
有了 String 方法后,输出变为自定义格式:
bash
Account 12345: $1000.00
这就是实现了 fmt.Stringer 接口。该接口定义如下:
go
type Stringer interface {
String() string
}
只要你的类型有 String() string 方法,就自动实现了这个接口,fmt.Println、fmt.Printf 等都会调用你的 String 方法。
知识点总结
| 知识点 | 关键概念 |
|---|---|
| 方法定义 | func (接收者 类型) 方法名(参数) 返回值,接收者写在函数名前 |
| 值接收者 | 操作拷贝,不修改原值,适合只读方法 |
| 指针接收者 | 操作原值,可以修改,适合需要变更的方法 |
| 同名方法 | 不同类型可以有同名方法,Go 根据调用对象类型自动选择 |
| String() 方法 | 实现 fmt.Stringer 接口,自定义打印格式 |
| 接收者一致性 | 一个类型应统一使用值接收者或指针接收者 |