Go結構體
Go的結構體是一種複合類型,它由一系列屬性(字段)組成,每個屬性(字段)都有自己的類型和值。結構體的字段可以是任何類型,甚至是結構體本身、函數、接口等
在Go中沒有類的概念,因此結構體經常被用來替代面向對象中的類的操作
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會幫我們自動轉換,非常方便,比較推薦