Go结构体
Go的结构体是一种复合类型,它由一系列属性(字段)组成,每个属性(字段)都有自己的类型和值。结构体的字段可以是任何类型,甚至是结构体本身、函数、接口等
在Go中没有类的概念,因此结构体经常被用来替代面向对象中的类的操作:Go 变通实现class效果
Go结构体的语法
结构体的定义格式如下:
type structName struct {
field1 type1
field2 type2
...
}
下面是一个结构体的实例,首先定义了student
这一结构体,其中包含了name
和id
两个属性。在main
函数中创建了一个student
的实例,并分别为它的两个属性赋值
type student struct{
name string
id int
}
func main(){
s := student{}
s.id = 1
s.name = "jay"
}
除了上面这种先创建后赋值的方式,也可以用下面的方式创建的同时为结构体的属性赋值
func main(){
a := student{
name: "tom",
id: 2,
}
}
结构体嵌套
在结构体内嵌使用结构体类型的字段,如下面代码所示,student
结构体中包括了一个person
结构体类型的字段,访问子结构体中的字段时需要一层一层嵌套调用s.p.sex
还有一种内嵌结构体,可以实现类似继承的效果
struct person{
sex int
}
struct student{
name string
id int
p person
}
func main(){
s := student{
name : "tongxue",
id : 1,
p : person{sex : 0},
}
s.p.sex = 1
}
匿名结构体
Go中支持匿名结构体的操作,即不需要事先定义声明某个的结构体类型,而直接在用到的时候定义并赋值
func main(){
c := struct {
name string
id int
}{
name : "Mike",
id : 1
}
}
在结构体中使用匿名的方式定义结构体字段:
type teacher struct {
name string
address struct {
city, stress string
}
}
func main(){
d := teacher{
name: "jake",
}
d.address.city = "guangzhou"
d.address.stress = "wushanRoad"
}
在上面的代码中,address
结构体没有提前定义,而是在定义teacher
时定义的。要特别注意在这种结构中要对内部的结构体字段进行赋值的话,不能在创建的时候赋值,只能在创建之后使用一层一层嵌套的访问方式去赋值d.address.city = "guangzhou"
匿名字段
结构体中可以包括一个或多个匿名字段,即这些字段没有显示的名字,只有字段的类型,此时默认字段的类型就是字段的名字(不推荐(*  ̄︿ ̄))
- 在一个结构体内,对于每一种类型只能有一个匿名字段
- 为匿名字段赋值的时候要注意值和匿名类型的顺序要一一对应
type killer struct{
string
int
}
func main(){
k := killer{"007", 111}
k.int = 222
k.string = "092"
}
内嵌结构体
同样地,匿名字段可以是一个结构体类型,即在结构体中可以嵌套匿名结构体字段,这种形式叫做内嵌结构体。内嵌结构体有点类似于面向对象语言中的继承行为,使用内嵌结构体,可以通过外部结构体直接访问到内嵌结构体中的字段(类似于继承了父类中的属性),这一点要和上面的结构体中包含结构体字段时需要一层一层嵌套访问的方式作区分
type person struct {
sex int
}
type student struct {
person
name string
id int
}
func main(){
e := student{
name: "sdk",
id: 666,
person: person{sex: 1}, //默认结构名为变量名
}
e.sex = 0 //直接访问内嵌结构体的字段
}
命名冲突
结构体要求字段名称要唯一,如果结构体中包含同名字段怎么办?(同名字段可能来自于“继承”到内嵌结构体的字段)
- 如果同名字段是不同层次的,(比如结构体中某字段和内嵌结构体中的字段重名了),那么直接访问该字段会访问到外层结构体的字段,如果要访问内嵌结构体的同名字段需要一层一层嵌套的方式的访问
- 如果同名字段属于同一层次,将会引发一个错误(不使用没关系)。没有办法来解决这种问题引起的二义性,必须由程序员自己修正
结构体作函数的传入参数
在函数中传入结构体作参数,同样是值传递,在函数内修改结构体不会对原来的结构体有影响。如果确实需要在函数体内去修改结构体,有两种方式:
- 传递结构体指针作参数
func pointTest(s *student) {
s.name = "nike"
s.id = 99
fmt.Println(s)
}
func main(){
a := student{
name: "tom",
id: 2,
}
pointTest(&a)
}
- 在创建结构体的时候取地址
func pointTest(s *student) {
s.name = "nike"
s.id = 99
fmt.Println(s)
}
func main(){
b := &student{
name: "john",
id: 12,
}
pointTest(b)
}
特别要注意的是,如果采用第二种方式,当我们要访问结构体的字段时,比如student
中的name
字段,直接使用b.name = "xxx"
即可,和你直接创建一个结构体的访问方式是相同的。Go会帮我们自动转换,非常方便,比较推荐
结构体的可见性
go struct中只有大写字母开头的变量才会被其他包读取,同一个包中不受影响。