在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定义的方法了。

文章目录