2

golang中结构体方法的接收者类型问题

 2 years ago
source link: https://www.yangyanxing.com/article/golang-struct-methond-receiver.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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函数时会rrs打印该方法返回的字符串。在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}

}
  1. 出于性能考虑一般情况下结构体方法接收者为指针形式
  2. 指针方法和值方法都可以在指针或非指针上被调用
  3. String() 方法需要注意接收者类型,在使用print 方法时的参数要与结构体方法接收者类型相同

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK