共计 2073 个字符,预计需要花费 6 分钟才能阅读完成。
在golang中,struct是设计来存储一组同一个类型或不同类型的数据的一个数据集合,是一种值类型,所以传递时需要区分是否传递其指针。
interface也是一种数据结构,存储的是一组具有共性的方法,但是只做定义,其他类型只要实现了这些方法,就是实现了这个接口,可以类比JAVA中的抽象类。
使用struct时,可以直接使用func方法数据结构中使用的方法,但是为了解耦这种方式,还是推荐使用interface来传递方法。
混淆点一:struct或者struct的指针类型
1. golang会自动将对数据类型定义的方法转化为数据类型指针定义的方法
先来看一段代码
type TestStruct struct {}
// 给结构体绑定方法
func (t TestStruct) Test() {
fmt.Println("this is Test func")
}
// 给结构体指针绑定方法
func (t *TestStruct) TestPt() {
fmt.Println("this is Test func")
}
func main () {
// 通过结构体访问方法
t := TestStruct{}
t.Test()
// 访问结构体指针的方法,发现也可以访问
t.TestPt()
// 通过结构体指针访问方法,也没问题
pt := &TestStruct{}
pt.Test()
pt.TestPt()
}
原因是这样的,官方文档上有这样的解释:
Struct fields can be accessed through a struct pointer.
To access the field X of a struct when we have the struct pointer p we could write (*p).X. However, that notation is cumbersome, so the language permits us instead to write just p.X, without the explicit dereference.
上面这段话的意思就是说,结构体的字段可以使用结构体指针来获取,写法应该是 (*p).X,但是这种符号太大而笨重了,所以这门语言允许用 p.X 这种简洁清晰的写法来代替。直白一点说,就是你通过结构体访问字段跟通过结构体指针访问,作为使用者来说是一样的
所以 p.X 的 p 有可能是结构体,也有可能是结构体指针,使用者要自己注意区分。
2. 在方法里改动了属性值
接受者为struct时,作用的范围为方法内部
type TestStruct struct {
Name string
}
// 给结构体指针绑定方法
func (t TestStruct) SetName() {
t.Name = "李四"
}
func main () {
// 通过结构体访问方法
t := TestStruct{}
t.Name = "张三"
t.SetName()
fmt.Println(t.Name) // 输出张三
}
接受者为struct指针时,所有的实例的属性值都会发生变化
// 给结构体指针绑定方法
func (t *TestStruct) SetName() {
t.Name = "李四"
}
func main () {
// 通过结构体访问方法
t := TestStruct{}
t.Name = "张三"
t.SetName()
fmt.Println(t.Name) // 输出李四
}
混淆点二:构造函数返回interface类型和struct指针类型
- 定义两个interface
type Interface1 interface {
GetNum1 ()
}
type Interface2 interface {
GetNum2 ()
}
- 定义struct,并实现这两个interface
type MStruct struct {}
func (m *MStruct) GetNum1() {
fmt.Println("GetNum1")
}
func (m *MStruct) GetNum2() {
fmt.Println("GetNum2")
}
- 构造函数返回结构体指针类型
func NewMStructPt() *MStruct{
return &MStruct{}
}
func main () {
pt := NewMStructPt()
// 都能正常访问
pt.GetNum1()
pt.GetNum2()
}
- 构造函数返回结构体类型
这个的效果跟结构体指针的效果一致,参考上p.X 和 (*p).X 的解释
- 构造函数返回interface类型
// 返回了 Interface1 类型
func NewMStructInterface1() Interface1 {
return &MStruct{}
}
func main () {
p1 := NewMStructInterface1()
// 访问 Interface1 定义的接口没问题
pt.GetNum1()
// 访问 Interface2 定义的接口,报错,因为没有实现 Interface2
pt.GetNum2() // p1.GetNum2 undefined (type Interface1 has no field or method GetNum2)
}
实际使用时,大部分场景还是建议返回结构体指针类型,这样就可以使用所有 interface定义的方法了。