golang中结构体方法的接收者类型问题
golang 中结构体可以看成面向对象编程中的类,可以为结构体定义方法,注意这里的方法和函数的区别,函数的定义是没有接收者的,方法是有接收者(receiver)的,这里的接收者可以是实例指针形式或者实例形式,鉴于性能的原因,recv 最常见的是一个指向 receiver_type 的指针,(因为我们不想要一个实例的拷贝,如果按值调用的话就会是这样),特别是在 receiver 类型是结构体时,就更是如此了。
方法的定义
定义一个Persion的结构体,并且绑定一个sayname方法
package main import "fmt" type Person struct { name string age int } func (self Person) sayname() { fmt.Printf("My name is %s and age is %d \n", self.name, self.age) } func main() { p := Person{ "yangyanxing", 18, } p.sayname() }
这里运行的结果为 My name is yangyanxing and age is 18 , 我在定义 sayname方法时使用的是 func (self Person) sayname() 这里是和定义函数不同的地方在于函数名前面有个接收者(self Person), 这里我使用的是self, 由于self并不是go中的关键词,我是沿用python中的类的关键词,当然这里也可以任何有效的变量,如果之前是写java的,也可以使用this ,本质上相当于实例本身。
这里定义方法并没有使用指针方式,所以方法体里是操作的变量的拷贝,如果结构休比较大,或者说即使不大的话,对于性能要求比较高的系统也会有一些影响,所以一般情况下,会使用结构体指针形式定义方法.
func (self *Person) sayname() { fmt.Printf("My name is %s and age is %d \n", self.name, self.age) }
但是在调用的时候,既可以使用结构体变量本身,也可以使用指针
func main() { p := Person{ "yangyanxing", 18, } pt := &Person{"yyx", 20} p.sayname() pt.sayname() }
这里无论是p还是pt都可以正常的调用sayname 方法,指针方法和值方法都可以在指针或非指针上被调用,在golang内部是会自动转换的。
上面的sayname方法没有修改变量本身的值,如果需要修改变量值的话,那么就需要使用指针了.
package main import "fmt" type Person struct { name string age int } func (self Person) changeage(age int) { // 改变age self.age = age } func main() { p := Person{ "yangyanxing", 18, } fmt.Println(p) p.changeage(100) fmt.Println(p) }
上面的方法并不会将p的age修改为100
{yangyanxing 18} {yangyanxing 18}
这时需要将接收者改为指针形式
func (self *Person) changeage(age int) { // 改变age self.age = age }
String() 方法
使用java的应该都知道toString () 方法,使用python的也应该都知道 __str__ 方法, 这些方法用于打印对象本身,当调用类print函数时会打印该方法返回的字符串。在golang中的结构体也有String() 方法, 用于打印结构体
package main import "fmt" type Person struct { name string age int } func (self Person) String() string { return fmt.Sprintf("Person name is %s and age is %d", self.name, self.age) } func main() { p := Person{ "yangyanxing", 18, } fmt.Println(p) }
这里打印出 Person name is yangyanxing and age is 18
但是这里要注意,如果定义方法时使用的值形式,那么调用的时候,也必须使用值形式,如果定义时使用的是指针形式,那么要调用的时候也要使用结构体地址。
package main import "fmt" type Person struct { name string age int } func (self *Person) String() string { return fmt.Sprintf("Person name is %s and age is %d", self.name, self.age) } func main() { p := Person{"yangyanxing", 18} pt := &Person{"yyx", 20} fmt.Println(p) //{yangyanxing 18} fmt.Println(&p) //Person name is yangyanxing and age is 18 fmt.Println(pt) //Person name is yyx and age is 20 fmt.Println(*pt) //{yyx 20} }
总结
出于性能考虑一般情况下结构体方法接收者为指针形式
指针方法和值方法都可以在指针或非指针上被调用
String() 方法需要注意接收者类型,在使用print 方法时的参数要与结构体方法接收者类型相同