持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第12天,点击查看活动详情
在前面的章节中介绍过了Golang中的方法和函数,但是如果不进行比较,对于两者的认识还是很容易产生混淆。
一、概述
Go语言支持方法。Go方法与Go函数相似,但有一点不同,方法就是一个包含了接收者参数的函数。
- 在接收者参数的帮助下,该方法可以访问接收者的属性。
- 接收方可以是结构类型或非结构类型。
- 在代码中创建方法时,接收者和接收者类型必须出现在同一个包中。而且不允许创建一个方法,其中的接收者类型已经在另一个包中定义,包括像int、string等内建类型。如果您尝试这样做,那么编译器将抛出错误。
语法
func(reciver_name Type) method_name(parameter_list)(return_type){
// Code
}
在此,可以在方法内访问接收器。
示例
type person struct {
name string
age int8
}
//函数,求和
func sum(x,y int) int {
return x + y
}
//方法
func (p person) String() string{
return fmt.Sprintf("姓名:%s\n年龄:%d",p.name,p.age)
}
func main() {
//初始化并调用方法
p := person{name:"Leefs",age:18}
fmt.Println(p.String())
//调用函数
x,y := 10,20
s := sum(x,y)
fmt.Printf("sum=%d\n",s)
}
运行结果
姓名:Leefs
年龄:18
sum=30
总结
方法在定义的时候,会在func和方法名之间增加一个参数,这个参数就是接收者,这样我们定义的这个方法就和接收者绑定在了一起,称之为这个接收者的方法。
二、同名方法
在 Go 中,相同的名字的方法可以定义在不同的类型上,而相同名字的函数是不被允许的。如果在GO语言程序中添加一个同名函数,就会报错。但是在不同的结构体上面定义同名的方法就是可行的。
type person struct {
name string
age int8
}
//同名方法,返回姓名和年龄
func (p person) String() string {
return fmt.Sprintf("姓名:%s,年龄:%d",p.name,p.age)
}
type student struct {
name string
}
//同名方法,返回学生姓名
func (s student) String() string{
return fmt.Sprintf("学生姓名:%s",s.name)
}
func main() {
p := person{name: "Leefs",age: 18}
fmt.Println(p.String())
s := student{name: "Jeyoo"}
fmt.Println(s.String())
}
运行结果
姓名:Leefs,年龄:18
学生姓名:Jeyoo
三、值传递和引用传递
-
值传递:值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
-
引用传递:引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
示例
type person struct {
name string
}
//值传递
func (p person) changeName(name string){
p.name = name
}
//引用传递
func (p *person) changeNamePointer(name string){
p.name = name
}
func main() {
p := person{name:"Leefs"}
fmt.Printf("原始名称:%s\n",p.name)
//修改失败
p.changeName("Took")
fmt.Printf("通过值传递修改名称:%s\n",p.name)
//修改成功
p.changeNamePointer("Jeyoo")
fmt.Printf("通过引用传递修改名称:%s\n",p.name)
}
运行结果
原始名称:Leefs
通过值传递修改名称:Leefs
通过引用传递修改名称:Jeyoo
在调用方法的时候,传递的接收者本质上都是副本,只不过一个是这个值副本,一是指向这个值指针的副本。指针具有指向原有值的特性,所以修改了指针指向的值,也就修改了原有的值。我们可以简单的理解为值接收者使用的是值的副本来调用方法,而指针接收者使用实际的值来调用方法。
四、在方法中使用值接收器与在函数中使用值参数
当一个函数有一个值参数,它只能接受一个值参数。当一个方法有一个值接收器,它可以接受值接收器和指针接收器。
type Lesson struct {
Name string
}
//方法
func (lesson Lesson) PrintInfo() {
fmt.Println(lesson.Name)
}
//函数
func PrintInfo(lesson Lesson) {
fmt.Println(lesson.Name)
}
func main() {
lesson := Lesson{"《张宇基础30讲》"}
//调用函数
PrintInfo(lesson)
//调用方法
lesson.PrintInfo()
bPtr := &lesson
//调用函数
//PrintInfo(bPtr) // error
//调用方法
bPtr.PrintInfo()
}
在上面的程序中,使用值参数 PrintInfo(lesson) 来调用这个函数是合法的,使用值接收器来调用 lesson.PrintInfo() 也是合法的。
然后在程序中我们创建了一个指向 Lesson 的指针 bPtr ,通过使用指针接收器来调用 bPtr.PrintInfo() 是合法的,但使用值参数调用 PrintInfo(bPtr) 是非法的。
五、在非结构体上的方法
不仅可以在结构体类型上定义方法,也可以在非结构体类型上定义方法,但是有一个问题。为了在一个类型上定义一个方法,方法的接收器类型定义和方法的定义应该在同一个包中。
示例
package main
import "fmt"
type myInt int
func (a myInt) add(b myInt) myInt {
return a + b
}
func main() {
var x myInt = 50
var y myInt = 7
fmt.Println(x.add(y)) // 57
}
总结
方法和函数之间的差异:
| 方法 | 函数 |
|---|---|
| 包含接收器 | 不包含接收器 |
| 可以接受指针和值 | 不能同时接受指针和值 |
| 可以在程序中定义相同名称但不同类型的方法 | 程序中不允许定义相同名称但不同类型的函数 |