结构体
结构体
Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。结构体可以存储一组不同类型的数据,是一种复合类型,示例如下:
type Programmer struct {
Name string
Age int
Job string
Language []string
}
声明
结构体的声明非常简单,例子如下:
type Person struct {
name string
age int
}
结构体本身以及其内部的字段都遵守大小写命名的暴露方式。对于一些类型相同的字段,可以像如下方式声明:
type Rectangle struct {
height, width, area int
color string
}
提示
在声明结构体字段时,字段名与方法名不应该重复
创建
Go不存在构造方法,大多数情况下采用如下的方式来创建。
programmer := Programmer{
Name: "jack",
Age: 19,
Job: "coder",
Language: []string{"Go", "C++"},
}
初始化的时候就像map
一样指定字段名称再初始化字段值,不过也可以省略字段名称。
programmer := Programmer{
"jack",
19,
"coder",
[]string{"Go", "C++"}}
当省略字段名称时,就必须初始化所有字段,且必须按照声明的顺序初始化。
func NewProgrammer() Programmer {
return Programmer{
"jack",
19,
"coder",
[]string{"Go", "C++"}}
}
也可以编写一个函数来专门初始化结构体,这类函数通常有另一个名称:工厂方法,这也是为什么Go没有构造方法的原因。
组合
在Go中,结构体之间的关系是通过组合来表示的,可以显式组合,也可以匿名组合,后者使用起来更类似于继承,但本质上没有任何变化。例如:
显式组合的方式
type Person struct {
name string
age int
}
type Student struct {
p Person
school string
}
type Employee struct {
p Person
job string
}
在使用时需要显式的指定字段p
student := Student{
p: Person{name: "jack", age: 18},
school: "lili school",
}
fmt.Println(student.p.name)
而匿名组合可以不用显式的指定字段
type Person struct {
name string
age int
}
type Student struct {
Person
school string
}
type Employee struct {
Person
job string
}
匿名字段的名称默认为类型名,调用者可以直接访问该类型的字段和方法,但除了更加方便以外与第一种方式没有任何的区别。
student := Student{
Person: Person{name: "jack",age: 18},
school: "lili school",
}
fmt.Println(student.name)
指针
对于结构体指针而言,不需要解引用就可以直接访问结构体的内容,例子如下:
p := &Person{
name: "jack",
age: 18,
}
fmt.Println(p.age,p.name)
在编译的时候会转换为(*p).name
,(*p).age
,其实还是需要解引用,不过在编码的时候可以省去,算是一种语法糖。
标签
结构体标签是一种元编程的形式,结合反射可以做出很多奇妙的功能,格式如下
`key1:"val1" key2:"val2"`
标签是一种键值对的形式,使用空格进行分隔。结构体标签的容错性很低,如果没能按照正确的格式书写结构体,那么将会导致无法正常读取,但是在编译时却不会有任何的报错,下方是一个使用示例。
type Programmer struct {
Name string `json:"name"`
Age int `yaml:"age"`
Job string `toml:"job"`
Language []string `properties:"language"`
}
结构体标签最广泛的应用就是在各种序列化格式中的别名定义,标签的使用需要结合反射才能完整发挥出其功能。
空结构体
空结构体没有字段,不占用内存空间,可以通过unsafe.SizeOf
函数来计算占用的字节大小
func main() {
type Empty struct {
}
fmt.Println(unsafe.Sizeof(Empty{}))
}
输出
0
空结构体的使用场景有很多,比如之前提到过的,作为map
的值类型,可以将map
作为set
来进行使用,又或者是作为通道的类型,即代表一个不发送数据的通道。