方法是带有接收者的函数。本章将详细介绍 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())
}

值接收者 vs 指针接收者

值接收者

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 (接收者) 方法名() {}
值接收者操作副本
指针接收者操作原值
方法继承通过嵌套继承方法
方法重写子结构体重写父结构体方法