Go中的方法

254 阅读4分钟

这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战

方法

Go中的方法可以理解为一种特殊的函数, 只不过在函数定义中多了一个接收器

在使用这个方法时, 只能只用这个接收器指定的类型变量来调用

在接口中定义的函数, 也称为方法

函数与方法的区别与联系

函数没有接收器, 方法有接收器

函数可以直接调用, 方法必须由类型实例来调用, 或者通过方法表达式来调用

Go中的方法与属性是隔离的, 没结耦合在一起, 数据和行为相互独立

格式

注意: 接收器的类型不能是接口类型或指针类型, 可以是其它任何类型

 /*
 方法名前面的(p Person)为接收器
 有接收器的函数, 称为方法
 不能单独使用, 必须由接收器指定的类型来调用
 */
 func (接收器名字, 接收器类型)方法名(参数列表)(返回参数列表) {
   // 方法体
 }

定义已有类型

 // 不能为不同包下的类型定义方法如 int, float64等
 // 我们可以为它们定义一个新类型, 再定义方法
 type MyNewFloat64 MyFloat64
 type MyAliasFloat64 = MyFloat64
 ​
 // 为其它包中的类型定义一个新类型, 并调用它的方法
 var f MyFloat64 = 1.2
 f.printFloat64()
 ​
 // MyNewFloat64的变量不能调用MyFloat64的方法
   //var f2 MyNewFloat64 = 2.3
 ​
 // 通过类型别名定义的变量, 可以调用原类型中的方法
 var f3 MyAliasFloat64 = 3.4
 f3.printFloat64()

定义新类型

 // 定义一个结构体
 type Person struct {
    name string
 }
 ​
 // 值方法, 更改姓名
 func (p Person) changeName()  {
   p.name = "xiaosiming"
 }
 ​
 // 指针方法, 更改姓名
 func (p *Person) changeName2() {
   p.name = "xiaoshiming"
 }

选择器和方法表达式

 // 选择器 p.name, p.printName 来获取字段和方法这就是选择器
 // 选择器的类型是字段对应的类型或方法对应的类型
 p := Person{"shaosiming"}
 ​
 // 通过变量来调用方法 (选择器)
 p.sayHi("daozhi")
 ​
 // 通过类型来调用方法 (方法表达式)
 // 当通过类型来调用方法时, 接收器变为这个方法的第一个参数, 参数列表中的参数后移, 从第二个开始
 Person.sayHi(p, "dasiming")
 ​
 // 通过选择器, 取到一个方法值, 赋值给一个变量
 pMethod := p.sayHi
 pMethod("genie")
 ​
 // 通过方法表达式, 取到一个函数值, 赋值给一个变量
 pMethod2 := Person.sayHi
 pMethod2(p, "ergouzi")

值方法, 指针方法

方法接收器中没有 , 则是值方法 方法接收器中有, 则是指针方法 在值方法中, 对接收器的修改, 不会影响到外面的值 在指针方法中, 对接收器的修改, 会影响到外面的值 建议: 使用指针方法

 // 定义一个结构体
 type Person struct {
    name string
 }
 ​
 // 值方法, 更改姓名
 /*
 p: 称为值接收器
 方法称为值方法
  */
 func (p Person) changeName()  {
   p.name = "xiaosiming"
 }
 ​
 // 指针方法, 更改姓名
 /*
 p: 称为指针接收器
 方法称为指针方法
 */
 func (p *Person) changeName2() {
   p.name = "xiaoshiming"
 }
 ​
 p2 := Person{"shaosiming"}
 fmt.Println("调用changeName前, p2的name: ", p2.name)
 p2.changeName()
 fmt.Println("调用changeName后, p2的name: ", p2.name)
 ​
 fmt.Println("调用changeName2前, p2的name: ", p2.name)
 p2.changeName2()
 fmt.Println("调用changeName2后, p2的name: ", p2.name)

方法的提升

 type Animal struct {
   name string
 }
 ​
 func (a Animal) run()  {
   fmt.Println(a.name, "动物在跑...")
 }
 ​
 func (a Animal) eat()  {
   fmt.Println(a.name, "动物在吃...")
 }
 ​
 func (a *Animal) sleep()  {
   fmt.Println(a. name, "动物在睡觉...")
 }
 ​
 type Dog struct {
   Animal
 }
 ​
 // 创建一个结构体变量
 a := Animal{"littel dog"}
 getMethodList(a)
 a.run()
 a.sleep()
 ​
 // 创建一个结构体变量, 这个结构体中的字段和方法全部继承自Animal结构体
 d := Dog{Animal{"big dog"}}
 ​
 /*
 当一个结构体中嵌入另一个匿名类型时, 匿名类型的字段和方法, 也同样会被内嵌
 */
 // 我们可以使用匿名类型名来当作字段名
 fmt.Println(d.Animal.name)
 d.Animal.run()
 d.Animal.sleep()
 ​
 // 也可以直接使用匿名类型中的字段和方法
 fmt.Println(d.name)
 d.run()
 d.sleep()

总结

当一个函数, 给它加了接收器之后, 就变成了一个方法. 可以使用类型变量来调用这个方法. 如果当作函数调用该方法的话, 接收器自动成为该方法的第一个参数, 其它参数后移. 当一个结构体中有匿名字段, 这个匿名字段中的所有字段和方法上升为当前结构体中的字段和方法, 也就是当前结构体变量可以直接访问匿名字段中的字段和方法, 不必通过匿名字段的类型名来访问.