您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“Golang中的Struct怎么定義和使用”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“Golang中的Struct怎么定義和使用”吧!
Go語(yǔ)言中提供了對(duì)struct的支持;struct
,中文翻譯稱(chēng)為結(jié)構(gòu)體
,與數(shù)組一樣,屬于復(fù)合類(lèi)型,并非引用類(lèi)型。Go語(yǔ)言的struct,與C語(yǔ)言中的struct或其他面向?qū)ο缶幊陶Z(yǔ)言中的類(lèi)(class)類(lèi)似,可以定義字段(屬性)和方法,但也有很不同的地方,需要深入學(xué)習(xí),才能區(qū)分他們之間的區(qū)別。
注意復(fù)合類(lèi)型與引用類(lèi)型之間的區(qū)別,這應(yīng)該也是值傳遞和引用傳遞的區(qū)別吧。
使用struct關(guān)鍵字可以定義一個(gè)結(jié)構(gòu)體,結(jié)構(gòu)體中的成員,稱(chēng)為結(jié)構(gòu)體的字段或?qū)傩浴?/p>
上面的代碼中,我們定義了一個(gè)包含5個(gè)字段的結(jié)構(gòu)體,可以看到,相同類(lèi)型 當(dāng)然,結(jié)構(gòu)體也可以不包含任何字段,稱(chēng)為type Member struct {
id int
name, email string
gender, age int
}
name
和email
、gender
和age
在同一行中定義,但比較好的編程習(xí)慣是每一行只定義一個(gè)字段,如:type Member struct {
id int
name string
email string
gender int
age int
}
空結(jié)構(gòu)體
,struct{}表示一個(gè)空的結(jié)構(gòu)體,注意,直接定義一個(gè)空的結(jié)構(gòu)體并沒(méi)有意義,但在并發(fā)編程中,channel之間的通訊,可以使用一個(gè)struct{}作為信號(hào)量。ch := make(chan struct{})
ch <- struct{}{}
上面的例子中,我們定義了Member結(jié)構(gòu)體類(lèi)型,接下就可以這個(gè)自定義的類(lèi)型創(chuàng)建變量了。
直接定義變量,這個(gè)使用方式并沒(méi)有為字段賦初始值,因此所有字段都會(huì)被自動(dòng)賦予自已類(lèi)型的零值,比如
name
的值為空字符串"",age的值為0。
var m1 Member//所有字段均為空值
使用字面量創(chuàng)建變量,這種使用方式,可以在大括號(hào)中為結(jié)構(gòu)體的成員賦初始值,有兩種賦初始值的方式,一種是按字段在結(jié)構(gòu)體中的順序賦值,下面代碼中
m2
就是使用這種方式,這種方式要求所有的字段都必須賦值,因此如果字段太多,每個(gè)字段都要賦值,會(huì)很繁瑣,另一種則使用字段名為指定字段賦值,如下面代碼中變量m3
的創(chuàng)建,使用這種方式,對(duì)于其他沒(méi)有指定的字段,則使用該字段類(lèi)型的零值作為初始化值。
var m2 = Member{1,"小明","xiaoming@163.com",1,18} // 簡(jiǎn)短變量聲明方式:m2 := Member{1,"小明","xiaoming@163.com",1,18}
var m3 = Member{id:2,"name":"小紅"}// 簡(jiǎn)短變量聲明方式:m3 := Member{id:2,"name":"小紅"}
通過(guò)變量名,使用逗號(hào)(.)
,可以訪問(wèn)結(jié)構(gòu)體類(lèi)型中的字段,或?yàn)樽侄钨x值,也可以對(duì)字段進(jìn)行取址(&)操作。
fmt.Println(m2.name)//輸出:小明
m3.name = "小花"
fmt.Println(m3.name)//輸出:小花
age := &m3.age
*age = 20
fmt.Println(m3.age)//20
結(jié)構(gòu)體與數(shù)組一樣,都是值傳遞,比如當(dāng)把數(shù)組或結(jié)構(gòu)體作為實(shí)參傳給函數(shù)的形參時(shí),會(huì)復(fù)制一個(gè)副本,所以為了提高性能,一般不會(huì)把數(shù)組直接傳遞給函數(shù),而是使用切片(引用類(lèi)型)代替,而把結(jié)構(gòu)體傳給函數(shù)時(shí),可以使用指針結(jié)構(gòu)體
。
指針結(jié)構(gòu)體,即一個(gè)指向結(jié)構(gòu)體的指針,聲明結(jié)構(gòu)體變量時(shí),在結(jié)構(gòu)體類(lèi)型前加*號(hào),便聲明一個(gè)指向結(jié)構(gòu)體的指針,如:
注意,指針類(lèi)型為引用類(lèi)型,聲明結(jié)構(gòu)體指針時(shí),如果未初始化,則初始值為nil,只有初始化后,才能訪問(wèn)字段或?yàn)樽侄钨x值。
另外,使用Go內(nèi)置new()函數(shù),可以分配內(nèi)存來(lái)初始化結(jié)構(gòu)休,并返回分配的內(nèi)存指針,因?yàn)橐呀?jīng)初始化了,所以可以直接訪問(wèn)字段。var m1 *Member
m1.name = "小明"//錯(cuò)誤用法,未初始化,m1為nil
m1 = &Member{}
m1.name = "小明"//初始化后,結(jié)構(gòu)體指針指向某個(gè)結(jié)構(gòu)體地址,才能訪問(wèn)字段,為字段賦值。
var m2 = new(Member)
m2.name = "小紅"
我們知道,如果將結(jié)構(gòu)體轉(zhuǎn)給函數(shù),只是復(fù)制結(jié)構(gòu)體的副本,如果在函數(shù)內(nèi)修改結(jié)構(gòu)體字段值,外面的結(jié)構(gòu)體并不會(huì)受影響,而如果將結(jié)構(gòu)體指針傳給函數(shù),則在函數(shù)中使用指針對(duì)結(jié)構(gòu)體所做的修改,都會(huì)影響到指針指向的結(jié)構(gòu)體。
func main() {
m1 := Member{}
m2 := new(Member)
Change(m1,m2)
fmt.Println(m1,m2)
}
func Change(m1 Member,m2 *Member){
m1.Name = "小明"
m2.Name = "小紅"
}
上面的例子中,我們定義結(jié)構(gòu)體字段名首字母是小寫(xiě)的,這意味著這些字段在包外不可見(jiàn)
,因而無(wú)法在其他包中被訪問(wèn),只允許包內(nèi)訪問(wèn)。
下面的例子中,我們將Member聲明在member包中,而后在main包中創(chuàng)建一個(gè)變量,但由于結(jié)構(gòu)體的字段包外不可見(jiàn),因此無(wú)法為字段賦初始值,無(wú)法按字段還是按索引賦值,都會(huì)引發(fā)panic錯(cuò)誤。
package member
type Member struct {
id int
name string
email string
gender int
age int
}
package main
fun main(){
var m = member.Member{1,"小明","xiaoming@163.com",1,18}//會(huì)引發(fā)panic錯(cuò)誤
}
因此,如果想在一個(gè)包中訪問(wèn)另一個(gè)包中結(jié)構(gòu)體的字段,則必須是大寫(xiě)字母開(kāi)頭的變量,即可導(dǎo)出的變量,如:
type Member struct {
Id int
Name string
Email string
Gender int
Age int
}
在定義結(jié)構(gòu)體字段時(shí),除字段名稱(chēng)和數(shù)據(jù)類(lèi)型外,還可以使用反引號(hào)為結(jié)構(gòu)體字段聲明元信息,這種元信息稱(chēng)為T(mén)ag,用于編譯階段關(guān)聯(lián)到字段當(dāng)中,如我們將上面例子中的結(jié)構(gòu)體修改為:
上面例子演示的是使用encoding/json包編碼或解碼結(jié)構(gòu)體時(shí)使用的Tag信息。type Member struct {
Id int `json:"id,-"`
Name string `json:"name"`
Email string `json:"email"`
Gender int `json:"gender,"`
Age int `json:"age"`
}
Tag由反引號(hào)括起來(lái)的一系列用空格分隔的key:"value"鍵值對(duì)組成,如:
Id int `json:"id" gorm:"AUTO_INCREMENT"`
下面總結(jié)幾點(diǎn)結(jié)構(gòu)體的相關(guān)特性:
結(jié)構(gòu)體與數(shù)組一樣,是復(fù)合類(lèi)型,無(wú)論是作為實(shí)參傳遞給函數(shù)時(shí),還是賦值給其他變量,都是值傳遞,即復(fù)一個(gè)副本。
Go語(yǔ)言是支持面向?qū)ο缶幊痰?,但卻沒(méi)有繼承的概念,在結(jié)構(gòu)體中,可以通過(guò)組合其他結(jié)構(gòu)體來(lái)構(gòu)建更復(fù)雜的結(jié)構(gòu)體。
一個(gè)結(jié)構(gòu)體,并沒(méi)有包含自身,比如Member中的字段不能是Member類(lèi)型,但卻可能是*Member。
在Go語(yǔ)言中,將函數(shù)綁定到具體的類(lèi)型中,則稱(chēng)該函數(shù)是該類(lèi)型的方法,其定義的方式是在func與函數(shù)名稱(chēng)之間加上具體類(lèi)型變量,這個(gè)類(lèi)型變量稱(chēng)為方法接收器
,如:
注意,并不是只有結(jié)構(gòu)體才能綁定方法,任何類(lèi)型都可以綁定方法,只是我們這里介紹將方法綁定到結(jié)構(gòu)體中。
從上面的例子中,我們可以看出,通過(guò)func setName(m Member,name string){//普通函數(shù)
m.Name = name
}
func (m Member)setName(name string){//綁定到Member結(jié)構(gòu)體的方法
m.Name = name
}
方法接收器
可以訪問(wèn)結(jié)構(gòu)體的字段,這類(lèi)似其他編程語(yǔ)言中的this關(guān)鍵詞,但在Go語(yǔ)言中,只是一個(gè)變量名而已,我們可以任意命名方法接收器
。
調(diào)用結(jié)構(gòu)體的方法,與調(diào)用字段一樣:
上面的代碼中,我們會(huì)很奇怪,不是調(diào)用setName()方法設(shè)置了字段Name的值了嗎?為什么還是輸出為空呢?m := Member{}
m.setName("小明")
fmt.Println(m.Name)//輸出為空
這是因?yàn)?,結(jié)構(gòu)體是值傳遞,當(dāng)我們調(diào)用setName時(shí),方法接收器接收到是只是結(jié)構(gòu)體變量的一個(gè)副本,通過(guò)副本對(duì)值進(jìn)行修復(fù),并不會(huì)影響調(diào)用者,因此,我們可以將方法接收器定義為指針變量,就可達(dá)到修改結(jié)構(gòu)體的目的了。
方法和字段一樣,如果首字母為小寫(xiě),則只允許在包內(nèi)可見(jiàn),在其他包中是無(wú)法訪問(wèn)的,因此,如果要在其他包中訪問(wèn)func (m *Member)setName(name string){/將Member改為*Member
m.Name = name
}
m := Member{}
m.setName("小明")
fmt.Println(m.Name)//小明
setName
,則應(yīng)該將方法名改為SetName
。
我們知道,結(jié)構(gòu)體中并沒(méi)有繼承的概念,其實(shí),在Go語(yǔ)言中也沒(méi)有繼承的概念,Go語(yǔ)言的編程哲學(xué)里,推薦使用組合
的方式來(lái)達(dá)到代碼復(fù)用效果。
組合,可以理解為定義一個(gè)結(jié)構(gòu)體中,其字段可以是其他的結(jié)構(gòu)體,這樣,不同的結(jié)構(gòu)體就可以共用相同的字段。
注意,在記得我們前面提過(guò)的,結(jié)構(gòu)體不能包含自身,但可能包含指向自身的結(jié)構(gòu)體指針。
例如,我們定義了一個(gè)名為Animal表示動(dòng)物,如果我們想定義一個(gè)結(jié)構(gòu)體表示貓,如:
可以看到,我們定義Cat結(jié)構(gòu)體時(shí),可以把Animal結(jié)構(gòu)體作為Cat的字段。type Animal struct {
Name string //名稱(chēng)
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()
}
上面的例子,我們看到,把Animal結(jié)構(gòu)體作為Cat的字段時(shí),其變量名為a,所以我們?cè)L問(wèn)Animal的方法時(shí),語(yǔ)法為c.a.Run()
,這種通過(guò)葉子屬性訪問(wèn)某個(gè)字段類(lèi)型所帶的方法和字段用法非常繁瑣。
Go語(yǔ)言支持直接將類(lèi)型作為結(jié)構(gòu)體的字段,而不需要取變量名,這種字段叫匿名字段
,如:
type Lion struct {
Animal //匿名字段
}
func main(){
var lion = Lion{
Animal{
Name: "小獅子",
Color: "灰色",
},
}
lion.Run()
fmt.Println(lion.Name)
}
通過(guò)上面例子,可以看到,通過(guò)匿名字段組合其他類(lèi)型,而后訪問(wèn)匿名字段類(lèi)型所帶的方法和字段時(shí),不需要使用葉子屬性,非常方便。
到此,相信大家對(duì)“Golang中的Struct怎么定義和使用”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢(xún),關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。