跳到主要内容

Method(方法)

方法是绑定到定义类型上的函数。接收者类型决定是否复制对象、是否能修改状态,以及 T*T 是否满足某个接口。

声明与调用

package main

import "fmt"

type Counter struct{ Value int }

func (c Counter) Current() int { return c.Value }
func (c *Counter) Increment() { c.Value++ }

func main() {
counter := Counter{}
counter.Increment() // 变量可寻址时,编译器会自动取址。
fmt.Println(counter.Current())
}

值接收者复制接收者,适合只读且复制成本可接受的类型;指针接收者操作原值,适合修改状态、避免复制大对象或包含锁的结构体。同一类型通常应统一选择接收者风格,避免调用者和接口使用者产生意外差异。

方法集与接口赋值

定义类型 T 的方法集只包含值接收者方法;*T 的方法集包含值接收者和指针接收者方法。因此下面的赋值只有第一行成立:

type Incrementer interface{ Increment() }

var value Counter
var a Incrementer = &value // *Counter 的方法集包含 Increment。
// var b Incrementer = value // 编译错误:Counter 没有指针接收者方法。

_ = a

编译器对普通变量调用时可以自动取址或解引用;接口调用不会把不匹配的方法集变成匹配。指针接收者为 nil 时,调用是否安全取决于方法体是否解引用它,不能只凭方法声明判断。

方法值与方法表达式

counter := Counter{Value: 3}
current := counter.Current // 方法值已绑定 counter。
read := Counter.Current // 方法表达式显式接收 Counter。

fmt.Println(current(), read(counter))

方法值会保存接收者;若接收者是值类型,绑定时会复制该值。需要明确调用时机或避免隐式复制时,使用方法表达式更直观。

常见错误

  • 用值接收者修改字段,实际修改的是副本。
  • T 作为接口值,却只给 *T 实现方法。
  • 对 nil 指针接收者直接解引用。
  • 复制包含 sync.Mutex 或其他不可复制状态的结构体。
  • 混用值接收者和指针接收者,使 API 的方法集难以预测。

TypeScript 的类方法通常通过对象引用访问;Go 没有类继承,方法属于定义类型,接收者选择还会影响接口实现。

小结

  • 值接收者读取副本,指针接收者可修改原值。
  • T*T 的方法集不同,这是接口设计的编译期边界。
  • 方法值绑定接收者,方法表达式把接收者作为显式参数。