php入門到就業線上直播課:進入學習
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API偵錯工具:
Go語言中提供了對struct的支援;struct
,中文翻譯稱為結構體
,與陣列一樣,屬於複合型別,並非參照型別。【相關推薦:Go視訊教學】
Go語言的struct,與C語言中的struct或其他物件導向程式語言中的類(class)類似,可以定義欄位(屬性)和方法,但也有很不同的地方,需要深入學習,才能區分他們之間的區別。
注意複合型別與參照型別之間的區別,這應該也是值傳遞和參照傳遞的區別吧。
使用struct關鍵字可以定義一個結構體,結構體中的成員,稱為結構體的欄位或屬性。
type Member struct {
id int
name, email string
gender, age int
}
登入後複製
上面的程式碼中,我們定義了一個包含5個欄位的結構體,可以看到,相同型別name
和email
、gender
和age
在同一行中定義,但比較好的程式設計習慣是每一行只定義一個欄位,如:
type Member struct {
id int
name string
email string
gender int
age int
}
登入後複製
當然,結構體也可以不包含任何欄位,稱為空結構體
,struct{}表示一個空的結構體,注意,直接定義一個空的結構體並沒有意義,但在並行程式設計中,channel之間的通訊,可以使用一個struct{}作為號誌。
ch := make(chan struct{})
ch <- struct{}{}
登入後複製
上面的例子中,我們定義了Member結構體型別,接下就可以這個自定義的型別建立變數了。
直接定義變數,這個使用方式並沒有為欄位賦初始值,因此所有欄位都會被自動賦予自已型別的零值,比如
name
的值為空字串"",age的值為0。
var m1 Member//所有欄位均為空值
登入後複製
使用字面量建立變數,這種使用方式,可以在大括號中為結構體的成員賦初始值,有兩種賦初始值的方式,一種是按欄位在結構體中的順序賦值,下面程式碼中
m2
就是使用這種方式,這種方式要求所有的欄位都必須賦值,因此如果欄位太多,每個欄位都要賦值,會很繁瑣,另一種則使用欄位名為指定欄位賦值,如下面程式碼中變數m3
的建立,使用這種方式,對於其他沒有指定的欄位,則使用該欄位型別的零值作為初始化值。
var m2 = Member{1,"小明","[email protected]",1,18} // 簡短變數宣告方式:m2 := Member{1,"小明","[email protected]",1,18}
var m3 = Member{id:2,"name":"小紅"}// 簡短變數宣告方式:m3 := Member{id:2,"name":"小紅"}
登入後複製
通過變數名,使用逗號(.)
,可以存取結構體型別中的欄位,或為欄位賦值,也可以對欄位進行取址(&)操作。
fmt.Println(m2.name)//輸出:小明
m3.name = "小花"
fmt.Println(m3.name)//輸出:小花
age := &m3.age
*age = 20
fmt.Println(m3.age)//20
登入後複製
結構體與陣列一樣,都是值傳遞,比如當把陣列或結構體作為實參傳給函數的形參時,會複製一個副本,所以為了提高效能,一般不會把陣列直接傳遞給函數,而是使用切片(參照型別)代替,而把結構體傳給函數時,可以使用指標結構體
。
指標結構體,即一個指向結構體的指標,宣告結構體變數時,在結構體型別前加*號,便宣告一個指向結構體的指標,如:
注意,指標型別為參照型別,宣告結構體指標時,如果未初始化,則初始值為nil,只有初始化後,才能存取欄位或為欄位賦值。
var m1 *Member
m1.name = "小明"//錯誤用法,未初始化,m1為nil
m1 = &Member{}
m1.name = "小明"//初始化後,結構體指標指向某個結構體地址,才能存取欄位,為欄位賦值。
登入後複製
另外,使用Go內建new()函數,可以分配記憶體來初始化結構休,並返回分配的記憶體指標,因為已經初始化了,所以可以直接存取欄位。
var m2 = new(Member)
m2.name = "小紅"
登入後複製
我們知道,如果將結構體轉給函數,只是複製結構體的副本,如果在函數內修改結構體欄位值,外面的結構體並不會受影響,而如果將結構體指標傳給函數,則在函數中使用指標對結構體所做的修改,都會影響到指標指向的結構體。
func main() {
m1 := Member{}
m2 := new(Member)
Change(m1,m2)
fmt.Println(m1,m2)
}
func Change(m1 Member,m2 *Member){
m1.Name = "小明"
m2.Name = "小紅"
}
登入後複製
上面的例子中,我們定義結構體欄位名首字母是小寫的,這意味著這些欄位在包外不可見
,因而無法在其他包中被存取,只允許包記憶體取。
下面的例子中,我們將Member宣告在member包中,而後在main包中建立一個變數,但由於結構體的欄位包外不可見,因此無法為欄位賦初始值,無法按欄位還是按索引賦值,都會引發panic錯誤。
package member
type Member struct {
id int
name string
email string
gender int
age int
}
package main
fun main(){
var m = member.Member{1,"小明","[email protected]",1,18}//會引發panic錯誤
}
登入後複製
因此,如果想在一個包中存取另一個包中結構體的欄位,則必須是大寫字母開頭的變數,即可匯出的變數,如:
type Member struct {
Id int
Name string
Email string
Gender int
Age int
}
登入後複製
在定義結構體欄位時,除欄位名稱和資料型別外,還可以使用反引號為結構體欄位宣告元資訊,這種元資訊稱為Tag,用於編譯階段關聯到欄位當中,如我們將上面例子中的結構體修改為:
type Member struct {
Id int `json:"id,-"`
Name string `json:"name"`
Email string `json:"email"`
Gender int `json:"gender,"`
Age int `json:"age"`
}
登入後複製
上面例子演示的是使用encoding/json包編碼或解碼結構體時使用的Tag資訊。
Tag由反引號括起來的一系列用空格分隔的key:"value"鍵值對組成,如:
Id int `json:"id" gorm:"AUTO_INCREMENT"`
登入後複製
下面總結幾點結構體的相關特性:
結構體與陣列一樣,是複合型別,無論是作為實參傳遞給函數時,還是賦值給其他變數,都是值傳遞,即復一個副本。
Go語言是支援物件導向程式設計的,但卻沒有繼承的概念,在結構體中,可以通過組合其他結構體來構建更復雜的結構體。
一個結構體,並沒有包含自身,比如Member中的欄位不能是Member型別,但卻可能是*Member。
在Go語言中,將函數繫結到具體的型別中,則稱該函數是該型別的方法,其定義的方式是在func與函數名稱之間加上具體型別變數,這個型別變數稱為方法接收器
,如:
注意,並不是只有結構體才能繫結方法,任何型別都可以繫結方法,只是我們這裡介紹將方法系結到結構體中。
func setName(m Member,name string){//普通函數
m.Name = name
}
func (m Member)setName(name string){//繫結到Member結構體的方法
m.Name = name
}
登入後複製
從上面的例子中,我們可以看出,通過方法接收器
可以存取結構體的欄位,這類似其他程式語言中的this關鍵詞,但在Go語言中,只是一個變數名而已,我們可以任意命名方法接收器
。
呼叫結構體的方法,與呼叫欄位一樣:
m := Member{}
m.setName("小明")
fmt.Println(m.Name)//輸出為空
登入後複製
上面的程式碼中,我們會很奇怪,不是呼叫setName()方法設定了欄位Name的值了嗎?為什麼還是輸出為空呢?
這是因為,結構體是值傳遞,當我們呼叫setName時,方法接收器接收到是隻是結構體變數的一個副本,通過副本對值進行修復,並不會影響呼叫者,因此,我們可以將方法接收器定義為指標變數,就可達到修改結構體的目的了。
func (m *Member)setName(name string){/將Member改為*Member
m.Name = name
}
m := Member{}
m.setName("小明")
fmt.Println(m.Name)//小明
登入後複製
方法和欄位一樣,如果首字母為小寫,則只允許在包內可見,在其他包中是無法存取的,因此,如果要在其他包中存取setName
,則應該將方法名改為SetName
。
我們知道,結構體中並沒有繼承的概念,其實,在Go語言中也沒有繼承的概念,Go語言的程式設計哲學裡,推薦使用組合
的方式來達到程式碼複用效果。
組合,可以理解為定義一個結構體中,其欄位可以是其他的結構體,這樣,不同的結構體就可以共用相同的欄位。
注意,在記得我們前面提過的,結構體不能包含自身,但可能包含指向自身的結構體指標。
例如,我們定義了一個名為Animal表示動物,如果我們想定義一個結構體表示貓,如:
type Animal struct {
Name string //名稱
Color string //顏色
Height float32 //身高
Weight float32 //體重
Age int //年齡
}
//奔跑
func (a Animal)Run() {
fmt.Println(a.Name + "is running")
}
//吃東西
func (a Animal)Eat() {
fmt.Println(a.Name + "is eating")
}
type Cat struct {
a Animal
}
func main() {
var c = Cat{
a: Animal{
Name: "貓貓",
Color: "橙色",
Weight: 10,
Height: 30,
Age: 5,
},
}
fmt.Println(c.a.Name)
c.a.Run()
}
登入後複製
可以看到,我們定義Cat結構體時,可以把Animal結構體作為Cat的欄位。
上面的例子,我們看到,把Animal結構體作為Cat的欄位時,其變數名為a,所以我們存取Animal的方法時,語法為c.a.Run()
,這種通過葉子屬性存取某個欄位型別所帶的方法和欄位用法非常繁瑣。
Go語言支援直接將型別作為結構體的欄位,而不需要取變數名,這種欄位叫匿名欄位
,如:
type Lion struct {
Animal //匿名欄位
}
func main(){
var lion = Lion{
Animal{
Name: "小獅子",
Color: "灰色",
},
}
lion.Run()
fmt.Println(lion.Name)
}
登入後複製
通過上面例子,可以看到,通過匿名欄位組合其他型別,而後存取匿名欄位型別所帶的方法和欄位時,不需要使用葉子屬性,非常方便。
在Go語言程式設計中,結構體大概算是使用得最多的資料型別了,通過定義不同欄位和方法的結構體,抽象組合不同的結構體,這大概便是Go語言中對物件導向程式設計了。
原文地址:https://juejin.cn/post/6844903814168838151
更多程式設計相關知識,請存取:!!
以上就是詳解Golang中的Struct(結構體)的詳細內容,更多請關注TW511.COM其它相關文章!