方法是带有接收者的函数。本章将详细介绍 Go 语言中方法的定义和使用。
方法是关联到特定类型的函数。通过方法,可以为自定义类型添加行为。
方法 vs 函数:
函数: func 函数名(参数) 返回值 { }
方法: func (接收者) 方法名(参数) 返回值 { }
func (接收者 类型) 方法名(参数) 返回值 {
// 方法体
}
package main
import "fmt"
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)
}
func main() {
r := Rectangle{Width: 10, Height: 5}
fmt.Printf("面积: %.2f\n", r.Area())
fmt.Printf("周长: %.2f\n", r.Perimeter())
}
package main
import "fmt"
type Counter struct {
Value int
}
func (c Counter) Increment() {
c.Value++
}
func main() {
c := Counter{Value: 0}
c.Increment()
fmt.Printf("Value: %d\n", c.Value)
}
输出:
Value: 0
值接收者操作的是副本,不影响原值。
package main
import "fmt"
type Counter struct {
Value int
}
func (c *Counter) Increment() {
c.Value++
}
func main() {
c := Counter{Value: 0}
c.Increment()
fmt.Printf("Value: %d\n", c.Value)
}
输出:
Value: 1
指针接收者可以修改原值。
| 情况 | 推荐 |
|---|---|
| 需要修改接收者 | 指针接收者 |
| 接收者是大结构体 | 指针接收者 |
| 接收者是切片、map、chan | 值接收者 |
| 一致性 | 如果有指针接收者,都用指针接收者 |
package main
import "fmt"
type Person struct {
Name string
}
func (p Person) SayHello() {
fmt.Printf("Hello, I'm %s\n", p.Name)
}
func main() {
p := Person{Name: "张三"}
p.SayHello()
}
package main
import "fmt"
type Person struct {
Name string
}
func (p Person) SayHello() {
fmt.Printf("Hello, I'm %s\n", p.Name)
}
func main() {
p := &Person{Name: "张三"}
p.SayHello()
}
Go 会自动进行值和指针的转换。
| 接收者类型 | 值调用 | 指针调用 |
|---|---|---|
| 值接收者 | ✅ | ✅ |
| 指针接收者 | ❌ | ✅ |
package main
import "fmt"
type MyInt int
func (m MyInt) ValueMethod() {
fmt.Println("值方法")
}
func (m *MyInt) PointerMethod() {
fmt.Println("指针方法")
}
func main() {
var v MyInt = 10
v.ValueMethod()
v.PointerMethod()
var p *MyInt = &v
p.ValueMethod()
p.PointerMethod()
}
不能直接为内置类型定义方法,但可以定义别名:
package main
import "fmt"
type MyInt int
func (m MyInt) IsPositive() bool {
return m > 0
}
func (m MyInt) Double() MyInt {
return m * 2
}
func main() {
var n MyInt = 10
fmt.Printf("IsPositive: %v\n", n.IsPositive())
fmt.Printf("Double: %d\n", n.Double())
}
通过结构体嵌套,子结构体可以继承父结构体的方法:
package main
import "fmt"
type Animal struct {
Name string
}
func (a Animal) Speak() {
fmt.Printf("%s 发出声音\n", a.Name)
}
type Dog struct {
Animal
Breed string
}
func main() {
dog := Dog{
Animal: Animal{Name: "旺财"},
Breed: "金毛",
}
dog.Speak()
}
子结构体可以重写父结构体的方法:
package main
import "fmt"
type Animal struct {
Name string
}
func (a Animal) Speak() {
fmt.Printf("%s 发出声音\n", a.Name)
}
type Dog struct {
Animal
}
func (d Dog) Speak() {
fmt.Printf("%s 汪汪叫\n", d.Name)
}
func main() {
dog := Dog{Animal: Animal{Name: "旺财"}}
dog.Speak()
dog.Animal.Speak()
}
package main
import "fmt"
type Person struct {
Name string
}
func (p Person) SayHello() {
fmt.Printf("Hello, I'm %s\n", p.Name)
}
func main() {
p := Person{Name: "张三"}
f := p.SayHello
f()
}
package main
import "fmt"
type Person struct {
Name string
}
func (p Person) SayHello() {
fmt.Printf("Hello, I'm %s\n", p.Name)
}
func main() {
p := Person{Name: "张三"}
f := Person.SayHello
f(p)
}
package main
import "fmt"
type Account struct {
Owner string
Balance float64
}
func NewAccount(owner string, balance float64) *Account {
return &Account{
Owner: owner,
Balance: balance,
}
}
func (a *Account) Deposit(amount float64) {
a.Balance += amount
fmt.Printf("存入 %.2f,余额 %.2f\n", amount, a.Balance)
}
func (a *Account) Withdraw(amount float64) bool {
if a.Balance < amount {
fmt.Println("余额不足")
return false
}
a.Balance -= amount
fmt.Printf("取出 %.2f,余额 %.2f\n", amount, a.Balance)
return true
}
func (a Account) GetBalance() float64 {
return a.Balance
}
func main() {
account := NewAccount("张三", 1000)
fmt.Printf("开户: %s, 余额: %.2f\n", account.Owner, account.Balance)
account.Deposit(500)
account.Withdraw(200)
account.Withdraw(2000)
}
本章学习了 Go 语言的方法:
| 知识点 | 说明 |
|---|---|
| 定义 | func (接收者) 方法名() {} |
| 值接收者 | 操作副本 |
| 指针接收者 | 操作原值 |
| 方法继承 | 通过嵌套继承方法 |
| 方法重写 | 子结构体重写父结构体方法 |