1. 方法定义

Golang 方法总是绑定对象实例,并隐式将实例作为第一实参 (receiver)。

  1. 只能为当前包内命名类型定义方法。
  2. 参数 receiver 可任意命名。如方法中未曾使用 ,可省略参数名。
  3. 参数 receiver 类型可以是 T *T。基类型 T 不能是接口或指针。
  4. 不支持方法重载,receiver 只是参数签名的组成部分。
  5. 可用实例 value pointer 调用全部方法,编译器自动转换。

一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。

所有给定类型的方法属于该类型的方法集。

1.1.1. 方法定义:

  1. func (recevier type) methodName(参数列表)(返回值列表){}
  2. 参数和返回值可以省略
  1. package main
  2. type Test struct{}
  3. // 无参数、无返回值
  4. func (t Test) method0() {
  5. }
  6. // 单参数、无返回值
  7. func (t Test) method1(i int) {
  8. }
  9. // 多参数、无返回值
  10. func (t Test) method2(x, y int) {
  11. }
  12. // 无参数、单返回值
  13. func (t Test) method3() (i int) {
  14. return
  15. }
  16. // 多参数、多返回值
  17. func (t Test) method4(x, y int) (z int, err error) {
  18. return
  19. }
  20. // 无参数、无返回值
  21. func (t *Test) method5() {
  22. }
  23. // 单参数、无返回值
  24. func (t *Test) method6(i int) {
  25. }
  26. // 多参数、无返回值
  27. func (t *Test) method7(x, y int) {
  28. }
  29. // 无参数、单返回值
  30. func (t *Test) method8() (i int) {
  31. return
  32. }
  33. // 多参数、多返回值
  34. func (t *Test) method9(x, y int) (z int, err error) {
  35. return
  36. }
  37. func main() {}

下面定义一个结构体类型和该类型的一个方法:

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. //结构体
  6. type User struct {
  7. Name string
  8. Email string
  9. }
  10. //方法
  11. func (u User) Notify() {
  12. fmt.Printf("%v : %v \n", u.Name, u.Email)
  13. }
  14. func main() {
  15. // 值类型调用方法
  16. u1 := User{"golang", "golang@golang.com"}
  17. u1.Notify()
  18. // 指针类型调用方法
  19. u2 := User{"go", "go@go.com"}
  20. u3 := &u2
  21. u3.Notify()
  22. }

输出结果:

  1. golang : golang@golang.com
  2. go : go@go.com

解释: 首先我们定义了一个叫做 User 的结构体类型,然后定义了一个该类型的方法叫做 Notify,该方法的接受者是一个 User 类型的值。要调用 Notify 方法我们需要一个 User 类型的值或者指针。

在这个例子中当我们使用指针时,Go 调整和解引用指针使得调用可以被执行。注意,当接受者不是一个指针时,该方法操作对应接受者的值的副本(意思就是即使你使用了指针调用函数,但是函数的接受者是值类型,所以函数内部操作还是对副本的操作,而不是指针操作。

我们修改 Notify 方法,让它的接受者使用指针类型:

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. //结构体
  6. type User struct {
  7. Name string
  8. Email string
  9. }
  10. //方法
  11. func (u *User) Notify() {
  12. fmt.Printf("%v : %v \n", u.Name, u.Email)
  13. }
  14. func main() {
  15. // 值类型调用方法
  16. u1 := User{"golang", "golang@golang.com"}
  17. u1.Notify()
  18. // 指针类型调用方法
  19. u2 := User{"go", "go@go.com"}
  20. u3 := &u2
  21. u3.Notify()
  22. }

输出结果:

  1. golang : golang@golang.com
  2. go : go@go.com

注意:当接受者是指针时,即使用值类型调用那么函数内部也是对指针的操作。

方法不过是一种特殊的函数,只需将其还原,就知道 receiver T 和 *T 的差别。

  1. package main
  2. import "fmt"
  3. type Data struct {
  4. x int
  5. }
  6. func (self Data) ValueTest() { // func ValueTest(self Data);
  7. fmt.Printf("Value: %p\n", &self)
  8. }
  9. func (self *Data) PointerTest() { // func PointerTest(self *Data);
  10. fmt.Printf("Pointer: %p\n", self)
  11. }
  12. func main() {
  13. d := Data{}
  14. p := &d
  15. fmt.Printf("Data: %p\n", p)
  16. d.ValueTest() // ValueTest(d)
  17. d.PointerTest() // PointerTest(&d)
  18. p.ValueTest() // ValueTest(*p)
  19. p.PointerTest() // PointerTest(p)
  20. }

输出:

  1. Data: 0xc42007c008
  2. Value: 0xc42007c018
  3. Pointer: 0xc42007c008
  4. Value: 0xc42007c020
  5. Pointer: 0xc42007c008

1.1.2. 普通函数与方法的区别

1.对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然。

2.对于方法(如struct的方法),接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以。

  1. package main
  2. //普通函数与方法的区别(在接收者分别为值类型和指针类型的时候)
  3. import (
  4. "fmt"
  5. )
  6. //1.普通函数
  7. //接收值类型参数的函数
  8. func valueIntTest(a int) int {
  9. return a + 10
  10. }
  11. //接收指针类型参数的函数
  12. func pointerIntTest(a *int) int {
  13. return *a + 10
  14. }
  15. func structTestValue() {
  16. a := 2
  17. fmt.Println("valueIntTest:", valueIntTest(a))
  18. //函数的参数为值类型,则不能直接将指针作为参数传递
  19. //fmt.Println("valueIntTest:", valueIntTest(&a))
  20. //compile error: cannot use &a (type *int) as type int in function argument
  21. b := 5
  22. fmt.Println("pointerIntTest:", pointerIntTest(&b))
  23. //同样,当函数的参数为指针类型时,也不能直接将值类型作为参数传递
  24. //fmt.Println("pointerIntTest:", pointerIntTest(&b))
  25. //compile error:cannot use b (type int) as type *int in function argument
  26. }
  27. //2.方法
  28. type PersonD struct {
  29. id int
  30. name string
  31. }
  32. //接收者为值类型
  33. func (p PersonD) valueShowName() {
  34. fmt.Println(p.name)
  35. }
  36. //接收者为指针类型
  37. func (p *PersonD) pointShowName() {
  38. fmt.Println(p.name)
  39. }
  40. func structTestFunc() {
  41. //值类型调用方法
  42. personValue := PersonD{101, "hello world"}
  43. personValue.valueShowName()
  44. personValue.pointShowName()
  45. //指针类型调用方法
  46. personPointer := &PersonD{102, "hello golang"}
  47. personPointer.valueShowName()
  48. personPointer.pointShowName()
  49. //与普通函数不同,接收者为指针类型和值类型的方法,指针类型和值类型的变量均可相互调用
  50. }
  51. func main() {
  52. structTestValue()
  53. structTestFunc()
  54. }

输出结果:

  1. valueIntTest: 12
  2. pointerIntTest: 15
  3. hello world
  4. hello world
  5. hello golang
  6. hello golang