iT邦幫忙

2022 iThome 鐵人賽

DAY 20
0
自我挑戰組

跟著 Go 實戰聖經 一起自學 Go系列 第 20

DAY 20 Go 語言 內嵌結構 (embedding struct)

  • 分享至 

  • xImage
  •  

昨天我們對於結構有基礎的了解後,今天繼續來學習結構還能怎麼變!

內嵌結構

雖然說 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 語言中的內嵌結構,明天繼續介紹幫自訂型別加上自己的函式或方法,明天見~~


上一篇
DAY 19 Go 語言 匿名結構 (anonymous strut) 與比較結構
下一篇
DAY 21 Go 語言 幫自訂型別加上自己的函式或方法(method)
系列文
跟著 Go 實戰聖經 一起自學 Go30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言