1. 前言
在Go语言中,函数和方法是不太一样的,函数是指不属于任何结构体、类型的方法,也就是说函数是没有接收者的; 而方法是有接收者的,要么是属于一个结构体的,要么属于一个新定义的类型的。
2. 方法和接收
在 Go 语言中,结构体就像是类的一种简化形式,Go的方法(Method)是一种作用于特定类型变量的函数。 这种特定类型变量叫做接收者(Receiver)。
// 接收者的类型可以是任何类型
// type ReceiverType int
type ReceiverType struct {
}
func (接收者变量 ReceiverType) 方法名(参数列表) (返回参数) {
函数体
}
参数和返回值可以省略
接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名的第一个小写字母。
接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型。
方法名、参数列表、返回参数:具体格式与函数定义相同。
package main
import "fmt"
type Person struct {
Name string
Age int
Address string
}
// Person的方法
func (p Person) PrintPerson() {
fmt.Printf(p.Name)
}
// NewPerson构造函数
func NewPerson(name string) *Person {
return &Person{
Name: name,
}
}
func main() {
NewPerson("雨中笑").PrintPerson() // 输出:雨中笑
}
3. 值类型的接收者
当方法作用于值类型接收者时,go语言会在代码运行时将接收者的值复制一份。在值类型接收者的方法中可以获取接收者的成员值,但修改操作只是针对副本,无法修改接收者变量本身。
package main
import "fmt"
type Person struct {
Name string
Age int
Address string
}
// NewPerson构造函数
func NewPerson(name string) *Person {
return &Person{
Name: name,
}
}
// 使用值接收者
func (p Person) SetName(name string) {
p.Name = name
}
func main() {
p := NewPerson("雨中笑")
fmt.Println(p.Name) // 雨中笑
p.SetName("你好")
fmt.Println(p.Name) // 雨中笑
}
4. 指针类型的接收者
指针类型的接收者由一个结构体的指针组成,在调用方法时修改接收者指针的任意成员变量,在方法结束后,修改都是有效的。
package main
import "fmt"
type Person struct {
Name string
Age int
Address string
}
// NewPerson构造函数
func NewPerson(name string) *Person {
return &Person{
Name: name,
}
}
// 使用指针接收者
func (p *Person) SetName(name string) {
p.Name = name
}
func main() {
p := NewPerson("雨中笑")
fmt.Println(p.Name) // 雨中笑
p.SetName("你好")
fmt.Println(p.Name) // 你好
}
对于什么时候使用指针类型接收者,我们可以根据:
需要修改接收者中的值
接收者是拷贝代价比较大的对象
保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。
5. 接收者为值类型、指针类型的变量互调方法
接收者为指针类型时,可以直接用值类型的变量调用方法,接收者为值类型时,可以直接用指针类型的变量调用方法。
package main
import "fmt"
type Person struct {
Name string
Age int
Address string
}
// 使用值类型接收者
func (p Person) Value() {
fmt.Printf("我是值类型接收者:%s;Value: %p \n", p.Name, &p)
}
// 使用指针类型接收者
func (p *Person) Point() {
fmt.Printf("我是指针类型接收者:%s;Point: %p \n\n", p.Name, p)
}
func main() {
// 值类型调用
pv := Person{"小明", 18, "广东"}
fmt.Printf("我是数据测试者:%s;Person: %p \n", pv.Name, &pv)
pv.Value()
pv.Point()
// 指针类型调用方法
pp := &Person{"小红",18, "杭州"}
fmt.Printf("我是数据测试者:%s;Person: %p \n", pp.Name, pp)
pp.Value()
pp.Point()
}
输出:
我是数据测试者:小明;Person: 0x11019a20
我是值类型接收者:小明;Value: 0x11019a40
我是指针类型接收者:小明;Point: 0x11019a20
我是数据测试者:小红;Person: 0x11019a60
我是值类型接收者:小红;Value: 0x11019a80
我是指针类型接收者:小红;Point: 0x11019a60
6. 任意类型添加方法
在Go语言中,接收者的类型可以是任何类型(但不能给别的包的类型定义方法)。
package main
import "fmt"
//MyString 将string定义为自定义MyString类型
type MyString string
//Say 为MyString添加一个Say的方法
func (m MyString) Say() {
fmt.Println("你好!")
}
func main() {
var m MyString
m.Say() //输出: 你好!。
m = "雨中笑"
fmt.Printf("%#v %T\n", m, m) //输出:"雨中笑" main.MyString
}
7. 对象的继承
go语言使用结构体实现其他编程语言中面向对象的继承。
package main
import "fmt"
type Person struct {
Name string
Age int
Address string
}
type Student struct {
Id int
*Person
}
func (s *Student) Say() {
fmt.Printf("Student-学生的id:%d\n", s.Id)
}
func (p *Person) Hello() {
fmt.Printf("Person-学生的信息-姓名:%s\n", p.Name)
}
func main() {
student := &Student{
Id: 123,
Person: &Person{
Name: "小明",
Age: 18,
Address: "广东",
},
}
student.Say() //输出:Student-学生的id:123
student.Hello() //输出:Person-学生的信息-姓名:小明
}