Golang - 函数和方法的区别【作用于值类型、指针类型】
在其他语言中,比如Java一般来说,函数就是方法,方法就是函数;
但是在Go语言中,函数和方法不太一样,有明确的概念区分。函数是指不属于任何结构体类型的方法,即函数是没有接受者的;而方法是有接收者的。
1、函数
func add (a, b int ) int {
return a+b
}
2、方法(特殊的函数)
func (t *T) add (a, b int ) int {
return a+b
}
其中T是自定义类型或者结构体,不能是基础类型int等。
3、方法的接收者
Go语言里方法有两种类型的接收者:值接收者和指针接收者。
1)、使用值类型接收者定义的方法,在调用的时候,使用的其实是值接收者的一个副本,所以对该值的任何操作,不会影响原来的类型变量。
func main() {
p:=person{name:"张三"}
p.modify() //值接收者,修改无效
fmt.Println(p.String())
}
type person struct {
name string
}
func (p person) String() string{
return "the person name is "+p.name
}
func (p person) modify(){
p.name = "李四"
}
2)、如果我们使用一个指针作为接收者,那么就会起作用,因为指针接收者传递的是一个指向原值指针的副本,指针的副本指向的还是原来类型的值,所以修改时,同时也会影响原来类型变量的值。
func main() {
p:=person{name:"张三"}
p.modify() //指针接收者,修改有效
fmt.Println(p.String())
}
type person struct {
name string
}
func (p person) String() string{
return "the person name is "+p.name
}
func (p *person) modify(){
p.name = "李四"
}
总结:
在调用方法的时候,传递的接收者本质上都是副本,一个是这个值副本,另一个是指向这个值指针的副本。指针具有指向原有值的特性,所以修改了指针指向的值,也就修改了原有的值。我们可以简单的理解为值接收者使用的是值的副本来调用方法,而指针接收者使用实际的值来调用方法。
4、如何选择 P
和 *P
一般的判断标准是看副本创建的成本和需求:
1)、不想变量被修改。 如果你不想变量被函数和方法所修改,那么选择类型P。相反,如果想修改原始的变量,则选择*P;
2)、如果变量是一个大的struct或者数组,则副本的创建相对会影响性能,这个时候考虑使用*P,只创建新的指针,这个区别是巨大的;
3)、(不针对函数参数,只针对本地变量/本地变量)对于函数作用域内的参数,如果定义成P,Go编译器尽量将对象分配到栈上,而*P很可能会分配到对象上,这对垃圾回收会有影响。
5、函数、方法作用于不同类型接收者的使用场景差异
1)、对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递;反之亦然。(接收者为指针类型时,不能将值类型的数据直接传递)
//1.普通函数
//接收值类型参数的函数
func valueIntTest(a int) int {
return a + 10
}
//接收指针类型参数的函数
func pointerIntTest(a *int) int {
return *a + 10
}
func structTest1() {
a := 2
fmt.Println("valueIntTest:", valueIntTest(a))
//函数的参数为值类型,则不能直接将指针作为参数传递
//fmt.Println("valueIntTest:", valueIntTest(&a))
//compile error: cannot use &a (type *int) as type int in function argument
b := 5
fmt.Println("pointerIntTest:", pointerIntTest(&b))
//同样,当函数的参数为指针类型时,也不能直接将值类型作为参数传递
//fmt.Println("pointerIntTest:", pointerIntTest(b))
//compile error:cannot use b (type int) as type *int in function argument
}
2)、对于方法(如struct的方法),接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以。(接收者为指针类型和值类型的方法,指针类型和值类型的变量均可相互调用)
//2.方法
type Person struct {
id int
name string
}
//接收者为值类型
func (p Person) valueShowName() {
fmt.Println(p.name)
}
//接收者为指针类型
func (p *Person) pointShowName() {
fmt.Println(p.name)
}
func structTest2() {
//值类型调用方法
personValue := Person{101, "Will Smith"}
personValue.valueShowName()
personValue.pointShowName()
//指针类型调用方法
personPointer := &Person{102, "Paul Tony"}
personPointer.valueShowName()
personPointer.pointShowName()
//与普通函数不同,接收者为指针类型和值类型的方法,指针类型和值类型的变量均可相互调用
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性