昨天我們對於結構有基礎的了解後,今天繼續來學習結構還能怎麼變!
雖然說 Go 語言不是物件導向程式設計語言,所以沒有跟 class 一樣可以繼承,但是 Go 語言提供了一種跟繼承有異曲同工之妙的替代方案 內嵌(embedding) ,便是可以在結構型別中,內嵌其他結構。
內嵌不只是借用其他地方的欄位,而是當今天把結構 B 內嵌到 A 時, B 的欄位會被 提升(promoted) 成 A 的自身欄位 (又開始講我聽不懂的中文了) 其實說成白話就是,當內嵌時,你的就會變成我的,我的還是我的!
內嵌結構時,大致上跟定義欄位相同,唯一不同是不用指定欄位名稱,只需加上來源的結構型別名稱。
type <型別名稱> struct {
<欄位> <型別> // 跟正常定義欄位相同
<結構型別> // 內嵌結構型別(不用指定欄位名稱)
}
常理來說我們可以內嵌任何型別到結構中,但一般來說要內嵌的型別較常是自訂型別或是結構,因為內嵌核心型別並沒有什麼特別的用處。
<結構變數>.<內嵌(結構)型別>
<結構變數>.<內嵌結構型別>.<欄位名稱>
<結構變數>.<提升的欄位名稱>
若要存取提升的欄位名稱,要先確定這些欄位名稱都是獨一無二不重複的,因為如果跟目標欄位的變數名稱名稱相同,那這個內嵌的欄位就不會被提升,這時如果你還是需要存取該欄位,就只能使用內嵌結構型別,也就是上方第一種方式來存取。
補充:
在初始化欄位值時,不能直接對於內嵌結構的欄位賦值,要透過型別名稱賦予其初始值。
範例 4:
package main
import "fmt"
type id int // 自訂一個名為 name 的字串型別
type hunter struct { // 定義名為 hunter 的結構型別
role string
age int
}
type skill struct { // 定義名為 skill 的結構型別
ability string
clan string
}
type myFavorite struct { // 定義名為 myFavorite 的結構型別
id // 內嵌自訂型別
hunter // 內嵌結構
skill // 內嵌結構
}
func getMyFavorites() []myFavorite {
var favorite1 myFavorite // 定義 favorite1 為 myFavorite 的結構變數,沒給初始值就會是零值
favorite2 := myFavorite{}
favorite2.id = 2 // 賦予結構欄位初始值
favorite2.role = "奇犽"
favorite2.age = 12
favorite2.ability = "變化系"
favorite2.clan = "揍敵客"
favorite3 := myFavorite{
id: 3,
hunter: hunter{
role: "小傑",
age: 12,
},
skill: skill{
ability: "強化系",
clan: "富力士",
},
}
favorite4 := myFavorite{}
favorite4.id = 4
favorite4.hunter.role = "西索"
favorite4.hunter.age = 26
favorite4.skill.ability = "變化系"
favorite4.skill.clan = "莫羅"
return []myFavorite{favorite1, favorite2, favorite3, favorite4}
}
func main() {
favorites := getMyFavorites()
for i := 0; i < len(favorites); i++ {
fmt.Printf("dot%v: %#v\n",i+1,favorites[i])
}
}
範例 4(執行結果):
dot1: main.myFavorite{id:0, hunter:main.hunter{role:"", age:0}, skill:main.skill{ability:"", clan:""}}
dot2: main.myFavorite{id:2, hunter:main.hunter{role:"奇犽", age:12}, skill:main.skill{ability:"變化系", clan:"揍敵客"}}
dot3: main.myFavorite{id:3, hunter:main.hunter{role:"小傑", age:12}, skill:main.skill{ability:"強化系", clan:"富力士"}}
dot4: main.myFavorite{id:4, hunter:main.hunter{role:"西索", age:26}, skill:main.skill{ability:"變化系", clan:"莫羅"}}
範例中使用內嵌結構與不同的賦值方式,優點是可以使用共同的資料架構,以及精簡程式碼; 但實際上我們並不常使用嵌入的做法,因為會需要考量欄位提升等等的問題。所以會比較偏好直接將結構當成目標的具名結構(結構包在結構中)會更簡單些。
補充:
內嵌型別時不能定義一個指標行為,但可以在內嵌時使用指標來指向指定的型別,但如此一來此內嵌結構型別的零值就會是 nil ,必須要賦與初始值。
type id *int // 將 id 改成 *int
embedded field type cannot be a pointer //錯誤訊息會直接告訴你,不能內嵌指標
type myFavorite struct {
id
hunter
*skill // 在內嵌時使用指標指向 skill
}
cannot use skill{…} (value of type skill) as type *skill in struct literal // 這邊一樣會報錯的原因是因為,若是將 skill 使用指標來指向時,零值就會是 nil ,必須要賦與初始值
今天介紹了 Go 語言中的內嵌結構,明天繼續介紹幫自訂型別加上自己的函式或方法,明天見~~