介绍
方法能给用户定义的类型添加新的行为。方法实际上也是函数,只是在声明时,在关键字 func 和方法名之间增加了一个参数。接收者
使用变量调用方法
使用
package mainimport "fmt"// user 在程序里定义了一个用户类型type user struct {name stringemail string}// notify 使用值接收者实现了一个方法func (u user) notify() {fmt.Printf("Sending User Email To %s<%s>\n",u.name,u.email)}// changeEmail 使用指针接收者实现了一个方法func (u *user) changeEmail(email string) {u.email = email}// main 是主程序入口func main() {// user 类型的值可以用来调用 使用值接收者声明的方法bill := user{"Bill", "bill@email.com"}bill.notify()// 指向 user 类型值的指针也可以用来调用 使用值接收者声明的方法lisa := &user{"Lisa", "lisa@email.com"}lisa.notify()// user 类型的值可以用来调用 使用指针接收者声明的方法bill.changeEmail("bill@newdomain.com")bill.notify()// user 类型的指针可以用来调用 使用指针接收者声明的方法lisa.changeEmail("lisa@comcast.com")lisa.notify()/*输出Sending User Email To Bill<bill@email.com>Sending User Email To Lisa<lisa@email.com>Sending User Email To Bill<bill@newdomain.com>Sending User Email To Lisa<lisa@comcast.com>*/}
值接收者和指针接收者
1、通过值接收者实现一个方法
调用时会使用这个值的副本来执行(相当于复制)
2、通过指针接收者实现一个方法
当使用指针接收者声明的方法时,这个方法会共享调用方法时接收者所指向的值。
(*lisa).notify() -> lisa.notify()
Go语言在背后将指针解引用为值。
使用值接收者还是指针接收者,不应该由该方法是否修改了接收到的值来决定,这个决策应该基于该类型的本质。
这条规则有一个例外,当需要让类型值符合某个接口的时候,即便类型的本质是非原始的,也可以选择使用值接收者声明方法。
- 内置类型:数值类型,字符串类型,布尔类型,这些类型本质上是原始的类型,因此,当对这些值进行增加或者删除的时候,会创建一个新值。因此这些类型的值传递给方法或者函数时,应该传递一个对应值的副本。使用值接收者传递副本。
- 引用类型:切片、映射、通道、接口和函数类型,它们的内存分配时连续的,创建这些类型的变量时,被称作标头值(head)(首地址),每个引用类型创建的标头值是包含一个指向底层数据结构的指针,每个引用类型还包含一组独特的字段,用于管理底层数据结构。标头值里包含一个指针,因此通过复制来传递一个引用类型的值的副本,本质上就是在共享底层数据结构。使用值接收者传递副本。
- 结构类型:结构类型可以用来描述一组数据值,这组值的本质即可以是原始的,也可以是非原始的。大多数情况下结构体的本质是非原始的,特别是内嵌类型结构,对这个类型的值做增加或者删除操作的时候应该更该值本身。使用指针接收者。
方法注意事项
1、结构体类型是值类型,在方法调用中,遵守值类型的传递机制(值拷贝)
2、也可以使用结构体指针的方式传递
3、方法作用在指定的数据类型上(和指定的数据类型绑定在一起),因此自定义类型都可以有方法,不仅仅是struct,比如 int , float32 … 都可以有方法
4、方法名大写的方法可以在别的包使用,否则只能在本包使用
5、如果一个类型实现了String()这个方法,那么fmt.Println默认会调用这个变量的String()方法输出。
package mainimport "fmt"type Student struct {Name stringAge int}// 给 Student 结构体 实现 String() 方法func (stu *Student) String() string {str := fmt.Sprintf("Name=[%v] Age=[%v]", stu.Name, stu.Age)return str}func main() {// 定义 Student 变量stu := Student{Name: "tom",Age: 20,}fmt.Println("stu = ", stu) // {tom 20}// 如果实现了 *Student类型的 String() 方法,fmt.Println就会自动调用fmt.Println("stu = ", &stu) // Name=[tom] Age=[20]}
