介绍

方法能给用户定义的类型添加新的行为。方法实际上也是函数,只是在声明时,在关键字 func 和方法名之间增加了一个参数。接收者
使用变量调用方法

使用

  1. package main
  2. import "fmt"
  3. // user 在程序里定义了一个用户类型
  4. type user struct {
  5. name string
  6. email string
  7. }
  8. // notify 使用值接收者实现了一个方法
  9. func (u user) notify() {
  10. fmt.Printf("Sending User Email To %s<%s>\n",
  11. u.name,
  12. u.email)
  13. }
  14. // changeEmail 使用指针接收者实现了一个方法
  15. func (u *user) changeEmail(email string) {
  16. u.email = email
  17. }
  18. // main 是主程序入口
  19. func main() {
  20. // user 类型的值可以用来调用 使用值接收者声明的方法
  21. bill := user{"Bill", "bill@email.com"}
  22. bill.notify()
  23. // 指向 user 类型值的指针也可以用来调用 使用值接收者声明的方法
  24. lisa := &user{"Lisa", "lisa@email.com"}
  25. lisa.notify()
  26. // user 类型的值可以用来调用 使用指针接收者声明的方法
  27. bill.changeEmail("bill@newdomain.com")
  28. bill.notify()
  29. // user 类型的指针可以用来调用 使用指针接收者声明的方法
  30. lisa.changeEmail("lisa@comcast.com")
  31. lisa.notify()
  32. /*
  33. 输出
  34. Sending User Email To Bill<bill@email.com>
  35. Sending User Email To Lisa<lisa@email.com>
  36. Sending User Email To Bill<bill@newdomain.com>
  37. Sending User Email To Lisa<lisa@comcast.com>
  38. */
  39. }

值接收者和指针接收者

1、通过值接收者实现一个方法

调用时会使用这个值的副本来执行(相当于复制)

2、通过指针接收者实现一个方法

当使用指针接收者声明的方法时,这个方法会共享调用方法时接收者所指向的值。
(*lisa).notify() -> lisa.notify()
Go语言在背后将指针解引用为值。

使用值接收者还是指针接收者,不应该由该方法是否修改了接收到的值来决定,这个决策应该基于该类型的本质。
这条规则有一个例外,当需要让类型值符合某个接口的时候,即便类型的本质是非原始的,也可以选择使用值接收者声明方法。

  1. 内置类型:数值类型,字符串类型,布尔类型,这些类型本质上是原始的类型,因此,当对这些值进行增加或者删除的时候,会创建一个新值。因此这些类型的值传递给方法或者函数时,应该传递一个对应值的副本。使用值接收者传递副本。
  2. 引用类型:切片、映射、通道、接口和函数类型,它们的内存分配时连续的,创建这些类型的变量时,被称作标头值(head)(首地址),每个引用类型创建的标头值是包含一个指向底层数据结构的指针,每个引用类型还包含一组独特的字段,用于管理底层数据结构。标头值里包含一个指针,因此通过复制来传递一个引用类型的值的副本,本质上就是在共享底层数据结构。使用值接收者传递副本。
  3. 结构类型:结构类型可以用来描述一组数据值,这组值的本质即可以是原始的,也可以是非原始的。大多数情况下结构体的本质是非原始的,特别是内嵌类型结构,对这个类型的值做增加或者删除操作的时候应该更该值本身。使用指针接收者。

方法注意事项

1、结构体类型是值类型,在方法调用中,遵守值类型的传递机制(值拷贝)
2、也可以使用结构体指针的方式传递
3、方法作用在指定的数据类型上(和指定的数据类型绑定在一起),因此自定义类型都可以有方法,不仅仅是struct,比如 int , float32 … 都可以有方法
4、方法名大写的方法可以在别的包使用,否则只能在本包使用
5、如果一个类型实现了String()这个方法,那么fmt.Println默认会调用这个变量的String()方法输出。

  1. package main
  2. import "fmt"
  3. type Student struct {
  4. Name string
  5. Age int
  6. }
  7. // 给 Student 结构体 实现 String() 方法
  8. func (stu *Student) String() string {
  9. str := fmt.Sprintf("Name=[%v] Age=[%v]", stu.Name, stu.Age)
  10. return str
  11. }
  12. func main() {
  13. // 定义 Student 变量
  14. stu := Student{
  15. Name: "tom",
  16. Age: 20,
  17. }
  18. fmt.Println("stu = ", stu) // {tom 20}
  19. // 如果实现了 *Student类型的 String() 方法,fmt.Println就会自动调用
  20. fmt.Println("stu = ", &stu) // Name=[tom] Age=[20]
  21. }