目录
一.引言
二.接收者类型
三.代码示例
1.指针接收者
2.值接收者
3.运行结果对比
4.代码修改
5.刨根问底
四.总结
一.引言
go 语言中 func (c *Title) 和 func (c Title) 两个方法的传参差一个 * 号,二者的区别是一个是指针类型,一个是值类型,下面我们简单整理下二者在 Func 中的差异和使用示例。
二.接收者类型
-
指针接收者 (
*Type
):- 当方法的接收者是指针类型时,方法可以修改接收者指向的对象的状态。
- 由于指针接收者可以避免复制整个对象,因此适合用于较大的结构体。
- 可以通过指针调用方法,或通过值调用方法,Go 会自动将值的地址传递给指针接收者。
-
值接收者 (
Type
):- 当方法的接收者是值类型时,方法接收者的值是通过值复制传递的,因此方法内部对接收者的任何修改不会影响到原始对象。
- 适合用于小型结构体或者值不变的方法。
- 只有值调用方法时才会调用值接收者的方法,不能通过指针调用该方法。
三.代码示例
1.指针接收者
package mainimport "fmt"// 定义结构体
type Title struct {title string
}// 使用指针接收者的方法
func (c *Title) SetTitle(title string) {c.title = title
}// 使用指针接收者的方法
func (c *Title) GetTitle() string {return c.title
}func main() {advisor := Title{}// 调用 SetTitle 方法(通过值调用)-- Go 会自动将地址传递给指针接收者advisor.SetTitle("New Title")// 调用 GetTitle 方法获取设置后的值fmt.Println(advisor.GetTitle())
}
2.值接收者
package mainimport "fmt"// 定义结构体
type Title struct {title string
}// 使用值接收者的方法
func (c Title) SetTitle(title string) {c.title = title
}// 使用值接收者的方法
func (c Title) GetTitle() string {return c.title
}func main() {advisor := Title{}// 调用 SetTitle 方法(通过值调用)advisor.SetTitle("New Title")// 调用 GetTitle 方法fmt.Println(advisor.GetTitle())
}
3.运行结果对比
- 在第一种情况下(指针接收者),调用
SetTitle
后,GetTitle
会返回"New Title"
,因为SetTitle
方法修改了title
字段。 - 在第二种情况下(值接收者),调用
SetTitle
后,GetTitle
仍然会返回空字符串""
,因为SetTitle
方法修改的是advisor
的副本,而不是原始对象。
4.代码修改
下面我们对值接收者代码做简单修改,使得我们 GetTitle 方法可以获得结果,由于值类型只能获取原始结构的信息,无法修改信息,所以 SetTitle 这里是不生效的,如果想要获取 title,我们在初始化结构体的时候就定义好 title 的值,这样就可以获取了。
package mainimport "fmt"// 定义结构体
type Title struct {title string
}// 使用值接收者的方法
func (c Title) SetTitle(title string) {c.title = title
}// 使用值接收者的方法
func (c Title) GetTitle() string {return c.title
}func main() {advisor := Title{}advisor.title = "New Title V2"// 调用 SetTitle 方法(通过值调用)advisor.SetTitle("New Title")// 调用 GetTitle 方法fmt.Println(advisor.GetTitle())
}
5.刨根问底
advisor := Title{}advisor.title = "New Title V2"
Q: 上面我们通过下述方法为 Title 类设置了标题,按照上面的思维,能够修改变量,那这里 advisor 是指针还是值呢?
A: advisor
不是指针,而是一个Title
结构体类型的实例。这个实例是值类型的,而不是指针类型的。
值类型
当使用 Title{}
初始化结构体实例时,产生的是一个值类型的变量。这意味着 advisor
变量直接存储结构体实例的数据。
package mainimport "fmt"type Title struct {title string
}func main() {// 初始化结构体实例,advisor 是 Title 类型的值advisor := Title{}// 设置字段值advisor.title = "New Title V2"// 输出fmt.Println(advisor.title) // 输出: New Title V2
}
指针类型
如果需要得到一个指针类型,可以使用 &
操作符,这样 advisor
变量将是一个指向 Title
结构体实例的指针。
func main() {// 初始化结构体实例,并获取其指针advisor := &Title{}// 通过指针来设置字段值advisor.title = "New Title V2"// 输出fmt.Println(advisor.title) // 输出: New Title V2
}
上面两个方法都会输出 "New Title V2",这里如果单纯构建结构体 Title 的话, Title 和 &TItle 是一样的,它们在初始化结构体实例方面并没有区别。显著的区别在于将实例传递到函数中时的行为。
package mainimport "fmt"type Title struct {title string
}func main() {// 值类型advisorValue := Title{}advisorValue.title = "ddd"fmt.Println("Value Type:", advisorValue.title)// 指针类型advisorPointer := &Title{}advisorPointer.title = "ddd"fmt.Println("Pointer Type:", advisorPointer.title)
}
上面两个方法达到的需求是一样的,那实际场景中我们该写哪种呢?
简单和小型结构体:
如果你的结构体很简单并且字段比较少(如本例中的 Title 结构体),且多数情况下仅做读取操作,使用值类型创建可以更加直观和简单。
推荐使用值类型:advisorValue := Title{}
修改结构体数据:
如果你的代码需要在多个函数中修改结构体字段,使用指针类型可以避免复制整个结构体,有助于提高性能。
推荐使用指针类型:advisorPointer := &Title{}
一致性:
如果整个代码库中大多数情况下都需要频繁对结构体进行修改,使用指针类型可以保持一致性,避免混淆。在这种情况下,可以统一使用指针类型。
我们上面的示例中,如果 title 写死了只做读取,那我们就 := Title,如果我们需要频繁修改 title 值,就是用 := &Title。
四.总结
- 指针接收者 用于需要修改接收者内部状态的方法,并且适合较大的结构体实例。
- 值接收者 用于不需要修改内部状态的方法,只适用于小型结构体或方法调用时不涉及修改操作。
选择使用指针接收者还是值接收者取决于您的具体需求和结构体的大小。对于需要修改内部状态、传递较大结构体的情况,推荐使用指针接收者。对于不修改状态的情况,可以使用值接收者。