Go语言方法(Go语言方法和接收器)
1 方法的定义
Go语言中的方法(
Method
)是一种作用于特定类型变量的函数。方法是与对象实例绑定的特殊函数。
这种特定类型变量叫做接收者(Receiver
)。接收者的概念就类似于其他语言中的this
或者 self
。
::: tip 方法定义语法
func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) { //方法体 } 复制代码
接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名的第一个小写字母,而不是self、this之类的命名。
接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型。
方法名、参数列表、返回参数:具体格式与函数定义相同。
::: 举例如下
package main import "fmt" type student struct { name string age int } type dog struct { name string } func (s *student) say(){ fmt.Printf("%s向大家问好\n", s.name) } func main() { //声明一个变量stu var stu student //变量赋值 stu.name="福小林" //调用student类型特定的方法say() stu.say() /* var d dog d.name="牧羊犬" //此处不能调用say(),报错d.say undefined (type dog has no field or method say) d.say() */ } 复制代码
1.1 指针类型的接收者
指针类型的接收者由一个结构体的指针组成,由于指针的特性,调用方法时修改接收者指针的任意成员变量,在方法结束后,修改都是有效的。这种方式就十分接近于其他语言中面向对象中的this或者self。 例如我们为student添加一个SetAge方法,来修改实例变量的年龄。
//定义一个student的结构体 type student struct { name string age int } func (s *student) SetAge(){ s.age++ } func main() { //声明一个变量stu var stu student //变量赋值 stu.name="张三" stu.age=18 fmt.Printf("调用SetAge()方法之前%s的年龄%d\n",stu.name,stu.age) //18 //调用student类型特定的方法调用SetAge() stu.SetAge() fmt.Printf("调用调用SetAge()方法后%s的年龄%d\n",stu.name,stu.age) //19 } 复制代码
1.2 值类型的接收者
当方法作用于值类型接收者时,Go语言会在代码运行时将接收者的值复制一份。在值类型接收者的方法中可以获取接收者的成员值,但修改操作只是针对副本,无法修改接收者变量本身。
type student struct { name string age int } func (s student) SetAge(){ s.age++ } func main() { //声明一个变量stu var stu student //变量赋值 stu.name="李四" stu.age=30 fmt.Printf("调用SetAge()方法之前%s的年龄%d\n",stu.name,stu.age) //30 //调用student类型特定的方法SetAge() stu.SetAge() fmt.Printf("调用SetAge()方法后%s的年龄%d\n",stu.name,stu.age) //30 } 复制代码
1.3 什么时候使用指针类型接收者
需要修改接收者中的值
接收者是拷贝代价比较大的大对象
保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。
2 为什么有函数了还需要方法呢
::: tip 原因
Go不是纯粹的面向对象编程语言,而且Go不支持类.因此基于类型的方法是一种实现和类相似行为的途径
相同的名字的方法可以定义在不同的类型上,而相同名字的函数是不被允许的。
:::
3 任意类型添加方法
在Go语言中,接收者的类型可以是任何类型,不仅仅是结构体,任何类型都可以拥有方法。 举个例子,我们基于内置的int类型使用type关键字可以定义新的自定义类型,然后为我们的自定义类型添加方法
//MyInt 将int定义为自定义MyInt类型 type MyInt int //SayHello 为MyInt添加一个SayHello的方法 func (m MyInt) SayHello() { fmt.Println("Hello, 我是一个int。") } func main() { var m1 MyInt m1.SayHello() //Hello, 我是一个int。 m1 = 100 fmt.Printf("%#v %T\n", m1, m1) //100 main.MyInt } 复制代码
注意:非本地类型不能定义方法,也就是说我们不能给别的包的类型定义方法。
4 结构体的“继承”
Go语言中使用结构体也可以模拟实现其他编程语言中面向对象的继承。
package main 复制代码
import "fmt"
//Animal 动物 type Animal struct { name string }
func (a Animal) move() { fmt.Printf("%s会动!\n", a.name) }
//Dog 狗 Dog包裹了Animal 模拟继承了Animal
type Dog struct { Feet int8 Animal //Animal拥有的方法,Dog也拥有了,因为Dog包裹了Animal,通过嵌套匿名结构体模拟实现继承 }
func (d Dog) wang() { fmt.Printf("%s会汪汪汪~\n", d.name) // d.name相当于 d.Animal.name }
func main() { d1 := Dog{ Feet: 4, Animal: Animal{ name: "旺财", }, } d1.wang() //旺财会汪汪汪~ d1.move() //旺财会动! }
## 函数和方法的区别 ::: danger 函数和方法的区别 1、普通函数:接收者(函数参数)为值类型时,不能将指针类型的数据直接传递,反之亦然。 ```go func function_name([parameter list]) [return_types] { /*函数体*/ } 复制代码
2、方法(如struct方法): 接收者为值类型时,可以直接用指针类型的变量调用方法,反之亦然。
func (variable_name variable_data_type) function_name() [return_type]{ /* 方法体*/ } 复制代码
:::
::: tip
方法与函数的区别是,函数不属于任何类型,方法属于特定的类型
方法是与对象实例绑定的特殊函数。
用于维护和展示对象自身的状态。对象是内敛的。普通函数则专注与算法流程,通过接受参数来完成特定的逻辑运算,并返回最终结果,方法是有关联状态的,函数通常是没有的。
方法和函数定义语法区别在于前者实例接受参数,编译器以此确定方法所属的类型。在一些语言中尽管没有定义,但是函数使用了隐式的传递this实例参数。
:::
作者:ourlang
链接:https://juejin.cn/post/7028768858294976543