结构体和方法

结构体是数组组织的一种形式,类似面向对象语言中的class。golang中使用结构体和结构体方法有面向对象的一些类似特性。

结构体定义

type 结构体名称 struct {
    字段名1 类型1  `字段标签`
    字段名2 类型2  `字段标签`
    ...
}

结构体字段名的大小写决定了这个结构体在包外是否可见。

type Stu struct{
    Name   string  //包外可见
    age     int    //包外不可见
}

结构体中的字段类型可以是任何类型,包括结构体本身,函数类型,接口类型。


type Inter interface{
    FuncA(int)
}

type Ftype func(int)

type S struct {
    fn   Ftype  //函数类型
    inter   Inter  //接口类型
    node    *S     //包含自己
    Name   string
}

结构体的标签可以通过结构体反射获取,在结构体序列化和反序列化时长会见到。 比如结构体需要使用json序列化和反序列化是可能需要如下定义:

type Stu struct{
    Name   string `json:"name"`
    No     string `json:"number"`
    age     int    //age对外不见不参与序列化
}

结构体初始化

type Stu struct{
    Name  string
    age   int
}

var stuA = Stu{"wida",18} //不带字段名初始化,必须按照结构体定义的顺序赋值
var stuB = Stu{Name:"amy",age:18} //带字段名初始化
var stuC = Stu{Name:"jenny"} //带字段名初始化,不给age初始化, age是int初始化值为0

一般在时间开发过程中,我们都建议使用带字段名初始化,这样防止在增加结构体字段的时候引起报错。

结构体还可以使用golang内置函数new初始化。使用new初始化返回的结构体指针。

var stuAPtr = new(Stu)
stuAPtr.Name = "wida"
stuAPtr.age = 18

大多数情况下我们可以使用取地址符&来取代new函数。

var stuAPtr = &Stu{"wida",18} //这个等效也上面的new 初始化

结构体嵌套

golang中结构体是可以嵌套的,A结构体嵌套Base结构体可以隐式获取Base结构体的属性,实际上A结构体有个匿名字段为Base在名字冲突的时候可以使用这个字段解决冲突问题


type Base struct {
    Name string
    Age int
}

type A struct{
    Base
    Gender int
}

type B struct {
    Base
    Name  string
}

var a A

a.Gender = 1    
a.Age = 18       //等同于a.Base.Age
a.Name = "wida" //等同于a.Base.Name

var b B
b.Age = 18
b.Name = "wida" //注意这个时候 b.Name是b自带的 而非b.Base.Name
b.Base.Name = "amy"

结构体方法

类似面向对象语言中的类方法,golang中结构体也有他的方法,在golang中结构体的方法有两中接收器(receiver),一种就是结构体结构体对象还一中是结构体结构体对象指针,需要注意二者在使用上的区别。

type Stu struct {
    Name string
    Age int
}

func (s Stu)GetName() string {
    return s.Name
}

func (s Stu)SetName(name string) {
    return s.Name = name
}

var a Stu

a.SetName("aaa")

fmt.Println(a) //{ 0} 这边SetName没有把a的Name赋值为aaa

SetName正确的写法应该是


func (s *Stu)SetName(name string) {
    return s.Name = name
}

a.SetName("aaa")  //这边go会有一个隐式转换 a->*a
fmt.Println(a) //{aaa 0}

为什么结构体对象做接收器SetName方法不起作用? 在golang中参数都是拷贝传递的,事实上接收器其实是特色的一个参数,结构体对象接收器的方法会把接收器对象拷贝一份,对新对象赋值操作当然不能改变老的对象,而由于指针结构体接收器拷贝的是指针,实际上是指向同一个对象,所以修改就能生效。 在实际开发过程中我们一般都会使用结构体对象指针接收器,不仅仅可以规避赋值不生效的问题,而且还可以防止大的对象发生拷贝的过程。

有个情况需要特别注意,在golang中nil是可以作为接收器的。

func (s *Stu) SetName(name string) {
	if s == nil {
		return
	}
	s.Name = name
}

var aPtr *Stu //aPtr是指针 初值是nil
aPtr.SetName("ddd") //代码没有效果,但是不会报错

方法继承

类似结构体字段的继承,结构体A内嵌另一个结构体B时,属于B的方法也会别A继承。

type Base struct {}

func (b *Base)Say() {
    fmt.Println("hi")
}

type A struct {
    Base
}

var a A
a.Say()