方法定义:
func (接收者 接收者类型) 方法名(参数列表) 返回值列表 {
// 方法体
}
接收者类型:
真实业务场景:银行账户管理系统 在金融应用中,账户对象需要封装各种操作(存款、取款、查询余额),这些操作需要维护账户状态的一致性,使用方法可以很好地封装这些行为。
package main
import (
"errors"
"fmt"
)
type BankAccount struct {
AccountNumber string
AccountHolder string
Balance float64
IsActive bool
}
// 值接收者方法 - 查询操作,不修改状态
func (acc BankAccount) GetAccountInfo() string {
status := "活跃"
if !acc.IsActive {
status = "冻结"
}
return fmt.Sprintf("账户: %s, 持有人: %s, 余额: ¥%.2f, 状态: %s",
acc.AccountNumber, acc.AccountHolder, acc.Balance, status)
}
// 指针接收者方法 - 需要修改账户状态
func (acc *BankAccount) Deposit(amount float64) error {
if !acc.IsActive {
return errors.New("账户已被冻结,无法存款")
}
if amount <= 0 {
return errors.New("存款金额必须大于0")
}
acc.Balance += amount
return nil
}
// 指针接收者方法 - 需要修改账户状态
func (acc *BankAccount) Withdraw(amount float64) error {
if !acc.IsActive {
return errors.New("账户已被冻结,无法取款")
}
if amount <= 0 {
return errors.New("取款金额必须大于0")
}
if amount > acc.Balance {
return errors.New("余额不足")
}
acc.Balance -= amount
return nil
}
// 指针接收者方法
func (acc *BankAccount) FreezeAccount() {
acc.IsActive = false
}
// 指针接收者方法
func (acc *BankAccount) ActivateAccount() {
acc.IsActive = true
}
func main() {
// 创建账户
account := BankAccount{
AccountNumber: "6222021234567890",
AccountHolder: "张三",
Balance: 1000.00,
IsActive: true,
}
// 调用值接收者方法
fmt.Println(account.GetAccountInfo())
// 调用指针接收者方法 - 存款
err := account.Deposit(500.0)
if err != nil {
fmt.Println("存款失败:", err)
} else {
fmt.Println("存款成功,当前余额:", account.Balance)
}
// 调用指针接收者方法 - 取款
err = account.Withdraw(200.0)
if err != nil {
fmt.Println("取款失败:", err)
} else {
fmt.Println("取款成功,当前余额:", account.Balance)
}
// 尝试取款超过余额
err = account.Withdraw(2000.0)
if err != nil {
fmt.Println("取款失败:", err)
}
// 冻结账户并尝试操作
account.FreezeAccount()
fmt.Println(account.GetAccountInfo())
err = account.Deposit(100.0)
if err != nil {
fmt.Println("操作失败:", err)
}
}
值接收者 vs 指针接收者的调用方式:
package main
import "fmt"
type Counter struct {
value int
}
// 值接收者方法
func (c Counter) GetValue() int {
return c.value
}
// 指针接收者方法
func (c *Counter) Increment() {
c.value++
}
func main() {
// 值类型变量
counter1 := Counter{value: 10}
// 指针类型变量
counter2 := &Counter{value: 20}
// 值类型可以调用值接收者和指针接收者方法
fmt.Println("counter1初始值:", counter1.GetValue()) // 值接收者方法
counter1.Increment() // 指针接收者方法,Go自动转换为(&counter1).Increment()
fmt.Println("counter1增值后:", counter1.GetValue())
// 指针类型也可以调用值接收者和指针接收者方法
fmt.Println("counter2初始值:", counter2.GetValue()) // 值接收者方法,Go自动转换为(*counter2).GetValue()
counter2.Increment() // 指针接收者方法
fmt.Println("counter2增值后:", counter2.GetValue())
// 方法集规则总结:
// - 值类型变量可以调用值接收者方法,也可以调用指针接收者方法
// - 指针类型变量可以调用值接收者方法,也可以调用指针接收者方法
}
选择值接收者还是指针接收者:
package main
import "fmt"
type Product struct {
ID int
Name string
Price float64
Quantity int
}
// 值接收者:适用于不修改结构体状态的查询方法
func (p Product) GetDisplayInfo() string {
return fmt.Sprintf("商品: %s, 价格: ¥%.2f", p.Name, p.Price)
}
// 值接收者:适用于返回新值的计算方法
func (p Product) CalculateTotalValue() float64 {
return p.Price * float64(p.Quantity)
}
// 指针接收者:需要修改结构体状态
func (p *Product) ApplyDiscount(discount float64) {
if discount > 0 && discount <= 1 {
p.Price = p.Price * (1 - discount)
}
}
// 指针接收者:需要修改结构体状态
func (p *Product) UpdateQuantity(newQuantity int) {
if newQuantity >= 0 {
p.Quantity = newQuantity
}
}
// 指针接收者:大结构体时提高性能
func (p *Product) UpdateProduct(name string, price float64) {
p.Name = name
p.Price = price
}
func main() {
product := Product{
ID: 1,
Name: "笔记本电脑",
Price: 5999.00,
Quantity: 10,
}
fmt.Println(product.GetDisplayInfo())
fmt.Printf("库存总价值: ¥%.2f\n", product.CalculateTotalValue())
product.ApplyDiscount(0.1) // 9折
fmt.Println("打折后:", product.GetDisplayInfo())
product.UpdateQuantity(15)
fmt.Printf("更新库存后总价值: ¥%.2f\n", product.CalculateTotalValue())
}