一文读懂 Go 方法

88 阅读2分钟

和其他纯粹的 OOP 的编程语言不同,Go 对面向对象的特性支持不多,Method(方法)是其中一种,我们可以显式的对一个类型 T 声明方法,只要这个类型符合以下条件

  • 必须是一个已经定义的类型
  • 必须和方法在同一个包中
  • 不能是指针类型
  • 不能是接口类型

对于每个声明的方法,编译器都会隐式声明对应的函数,这两个函数其实都是 TypeDenotation.MethodName 的形式,但是我们自己却不能在代码里主动声明,它只能由编译器声明,但是我们自己却能在代码里主动使用这种形式进行调用

type Book struct {
	pages int
}

func (b Book) Pages() int {
	return b.pages
}

func (b *Book) SetPages(pages int) {
	b.pages = pages
}

func Book.Pages(b Book) int {
	// Pages
	return b.pages
}

func (*Book).SetPages(b *Book, pages int) {
	// SetPages
	b.pages = pages
}

对于一个方法来说,*T 被称为指针接受者,T 被称为值接受者,虽然有的时候,我们只是声明了值类型接受者的方法,但是使用指针类型依然可以调用,这是为什么呢?这是因为对于每一个接受者是 T 的方法,编译器都会隐式声明一个接受者是*T 的方法

和通用的函数一样,接受者参数都是通过复制来进行传递的,所以在有些情况下,内部的修改不会影响到外部,所以如果需要修改外部的内容,那么就应该使用指针接受者

type Book struct {
    pages int
}

type Books []Book

func (b Books) Modify() {
    b[0].pages = 500
    b = append(b, Book{789})
}

func main() {
    var books = Books{{123}, {456}}
    books.Modify()
    fmt.Println(books)  // [{500} {456}]
}

所以什么时候该使用值接受者,什么时候又该使用指针接受者呢?其实任何时候都使用指针接受者在逻辑上是没有问题,但是有的时候考虑其他问题的时候,我们需要使用值接受者

  • 值接受者相比指针接受者大部分的时候都会占用更多的内存
  • 大量的指针接受者会对垃圾回收造成负担
  • sync 包中的内容是不能被复制的,所以涉及到的只能使用指针接受者