Go语言中方法(Method)是带有接收者(Receiver)的函数。接收者可以是任意自定义类型(通常是结构体),这让类型既有数据又有了行为,实现了面向对象编程风格的封装。方法与函数的区别在于:方法属于某个类型,调用时需要使用.运算符。
方法定义语法:func (receiver Type) MethodName(参数) 返回值 { ... }。接收者通常使用类型首字母小写命名。
package main
import "fmt"
type Rectangle struct {
Width, Height float64
}
// 值接收者方法:计算面积
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
// 指针接收者方法:缩放矩形
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
func main() {
rect := Rectangle{Width: 10, Height: 5}
fmt.Println("面积:", rect.Area()) // 面积: 50
rect.Scale(2) // 指针接收者自动取地址
fmt.Println("缩放后面积:", rect.Area()) // 缩放后面积: 200
}
type Counter struct {
count int
}
// 值接收者:不会改变原始值
func (c Counter) IncValue() {
c.count++
}
// 指针接收者:会改变原始值
func (c *Counter) IncPointer() {
c.count++
}
func main() {
c := Counter{count: 0}
c.IncValue()
fmt.Println(c.count) // 0(未改变)
c.IncPointer()
fmt.Println(c.count) // 1
}
方法可以视为一种特殊的函数,接收者相当于函数的第一个参数。Go也支持将方法转换为函数(显式传递接收者)。
// 方法
func (r Rectangle) Area() float64 { ... }
// 等效的函数
func Area(r Rectangle) float64 { ... }
// 调用对比
rect := Rectangle{10,5}
fmt.Println(rect.Area()) // 方法调用
fmt.Println(Area(rect)) // 函数调用
不仅结构体,任何自定义类型(包括基本类型别名、切片、map等)都可以定义方法,但不能为其他包的类型定义方法(除非定义别名)。
type MyInt int
func (m MyInt) Double() MyInt {
return m * 2
}
type StringSlice []string
func (ss StringSlice) Join(sep string) string {
return strings.Join(ss, sep)
}
func main() {
x := MyInt(5)
fmt.Println(x.Double()) // 10
arr := StringSlice{"a", "b", "c"}
fmt.Println(arr.Join("-")) // a-b-c
}
当一个结构体匿名嵌套其他类型时,外部结构体会自动获得嵌套类型的方法(方法提升),实现了类似继承的效果。
type Animal struct {
Name string
}
func (a Animal) Speak() string {
return "我是动物"
}
type Dog struct {
Animal // 匿名嵌套
Breed string
}
func main() {
dog := Dog{
Animal: Animal{Name: "旺财"},
Breed: "金毛",
}
// 可以直接调用 Animal 的方法
fmt.Println(dog.Speak()) // 我是动物
}
方法值:将方法绑定到特定接收者,赋值给变量,稍后调用。
方法表达式:将方法作为函数类型,需要显式传递接收者作为第一个参数。
type Point struct{ X, Y float64 }
func (p Point) Distance(q Point) float64 {
return math.Hypot(p.X-q.X, p.Y-q.Y)
}
func main() {
p1 := Point{1, 2}
p2 := Point{4, 6}
// 方法值:绑定接收者 p1
distanceFromP1 := p1.Distance
fmt.Println(distanceFromP1(p2)) // 5
// 方法表达式:未绑定接收者
distanceFunc := Point.Distance
fmt.Println(distanceFunc(p1, p2)) // 5
}
接收者的类型不能是接口类型,也不能是指针类型(但可以是指向自定义类型的指针)。方法定义必须与类型定义在同一个包内。
类型的方法集决定了该类型实现的接口。规则:
T 的方法集包含所有值接收者的方法。*T 的方法集包含所有值接收者 + 指针接收者的方法。type Foo struct{}
func (Foo) ValueMethod() {}
func (*Foo) PointerMethod() {}
var t Foo
var pt *Foo
// t 只能调用 ValueMethod
t.ValueMethod() // 可以
// t.PointerMethod() // 编译错误
// pt 可以调用两者
pt.ValueMethod() // 可以(Go自动解引用)
pt.PointerMethod() // 可以
package main
import "fmt"
type Account struct {
owner string
balance float64
}
// 构造函数
func NewAccount(owner string) *Account {
return &Account{owner: owner, balance: 0}
}
// 值接收者:查询余额
func (a Account) Balance() float64 {
return a.balance
}
// 指针接收者:存款
func (a *Account) Deposit(amount float64) bool {
if amount <= 0 {
return false
}
a.balance += amount
return true
}
// 指针接收者:取款
func (a *Account) Withdraw(amount float64) bool {
if amount <= 0 || amount > a.balance {
return false
}
a.balance -= amount
return true
}
// 值接收者:显示账户信息
func (a Account) String() string {
return fmt.Sprintf("Owner: %s, Balance: %.2f", a.owner, a.balance)
}
func main() {
acc := NewAccount("Alice")
acc.Deposit(1000)
acc.Withdraw(300)
fmt.Println(acc) // Owner: Alice, Balance: 700.00
fmt.Println("当前余额:", acc.Balance()) // 当前余额: 700
}