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的方法集不同,这是接口设计的编译期边界。- 方法值绑定接收者,方法表达式把接收者作为显式参数。