認真一點學 Go:12. 自定義型別和結構體 - 定義

printlove發表於2021-10-11

公眾號文章地址

學到什麼

  1. 如何自定義型別?

  2. 如何定義結構體?

  3. 如何初始化結構體?

  4. 如何巢狀結構體?

  5. 如何定義匿名結構體?

  6. 如何給型別取別名?

  7. 如何定義結構體標籤?

概念

什麼是自定義型別?當 Go 語言中內建的型別,例如:int、string 等等,不能滿足需求時,就可以自定義一個型別。

建立自定義型別

1. 基於內建型別


type typeName baseType
  • typeName 為定義的型別名稱

  • baseType 依賴的型別,Go 語言中所有的資料型別都可以,還有待會要講的結構體 struct

舉例,以下 3 個自定義的型別都依賴於內建型別。


type str string

type num int

type m map[string]string

在上例中,雖然 str 型別依賴 string 型別,但在 Go 語言中是強型別語言,也就是這兩個型別不能直接比較。

如果 strstring 型別相比較,就需要型別轉化,自定義的其它型別都是這樣。


var s1 str = "new string"

// str 轉化為 string 型別

s2 := string(s1)

2. 結構體

結構體是自定義型別中的複合型別,在這個型別中可以包含多個不同的資料型別。

定義了一個 People 型別的結構體,裡面包含了兩個型別欄位。


type People struct {

    // 欄位

    *Name string*

    Age  int

}

當欄位型別相同時,可以對相同的只宣告一次。


type StructName struct {

    Name string

    Age, Weight int

}

如果想把結構體中的欄位寫在一行,需要使用”英文分號”相隔,為了程式碼的結構清晰,這種一般不使用。


type OneLine struct{Name string; Age, Weight int}

結構體中也可以不定義任何欄位,即空結構體。


type EmptyStruct struct {}

注意點:

  • 使用 struct 關鍵字定義。

  • struct 關鍵字後必須緊跟 “{”,即在同一行。

初始化結構體

定義好結構體後,下來就需要初始化值。

1. 帶欄位名稱


p1 := People{

    Name: "老苗",

    Age:  18,

}

給欄位賦值時,也可以只設定一部分,也可以都不設定,沒有設定的會按照預設值走。


p := People{

    Age:  18,

}

exmaple := People{}

Name 預設為空字串,Age 預設為 0。

2. 不帶欄位名稱

在設定欄位值時,可以不帶欄位名稱,這時候就必須按照結構體欄位順序賦值。


p2 := People{

    "老苗",

    18,

}
  • 賦值時,不能進行部分省略。

  • 帶欄位名稱和不帶欄位名稱不能混合。

訪問結構體欄位

使用“點”訪問欄位值和設定欄位值


p := People{"老苗", 18}

// 訪問欄位

fmt.Println(p.Name)

// 設定欄位

p.Name = "瀟灑哥"

結構體巢狀

在一個結構體中,可以巢狀另外一個結構體。這個特性在物件導向中,有點類似繼承。


type People struct {

    Name string

    Age  int

}

type Student struct {

    People

    Collect string

}

Student 結構體中,巢狀了 People 結構體。巢狀時,可以不需要設定欄位名稱,這時候預設的欄位名稱為巢狀型別名稱。

1. 初始化

使用兩種方式初始化 Student 結構體,一種是帶欄位名稱,另一種是不帶欄位名稱。


// 第一種:帶欄位名稱

s1 := Student{

    People: People{

        Name: "老苗",

        Age:  18,

    },

    Collect: "不告訴",

}

// 第二種:不帶欄位名稱

s2 := Student{

    People{

        Name: "老苗",

        Age:  18,

    },

    "不告訴",

}

2. 訪問巢狀結構體

在上面的例子中,People 結構體嵌入到 Student 結構體中,並且沒有定義欄位名稱,這種情況獲取 People 結構體中的欄位就有兩種方式。

第一種:訪問不帶欄位名稱,因為PeopleStudent 結構體中的欄位會變成同一級,結構體攜帶的方法(下篇講解)也是一樣。


s2.Name

第二種:訪問帶欄位名稱,嵌入時不寫欄位名稱,預設的欄位名稱就是嵌入型別名。


s2.People.Name

3. 欄位名相同

當被嵌入結構體與父級結構體欄位名稱相同時,編譯器是可以通過的。例如 RepeatStudent 結構體中的Name 欄位與 People 結構體中的 Name 欄位名相同,這時候訪問 People 結構體中的 Name 欄位就必須帶上結構體名稱。


type RepeatStudent struct {

    People

    Collect string

    Name string

}

r := RepeatStudent{

    People: People{Name: "老苗"},

    Name:   "瀟灑哥",

}

fmt.Println(r.Name, r.People.Name)

// 輸出

瀟灑哥 老苗

匿名結構體

匿名結構體指的就是沒有結構體名稱,和匿名函式一樣都沒有名稱。


ano := struct {

   Name string

}{

   Name: "匿名",

}

該程式碼定義了一個匿名結構體幷包含了一個欄位,定義後再進行初始化。

在巢狀結構體時,也可以使用匿名結構體。


type AnoStudent struct {

    People struct {

        Name string

        Age  int

    }

    Collect string

}

結構體標籤

在定義結構體時,可以給欄位寫上標籤,通過標籤對結構體的進行自定義處理。

例如,使用標準包 “encoding/json” 轉 json 字串,通過標籤可以宣告將結構體欄位轉成對應的名稱。


type Tag struct {

    Name string `json:"name"`

}

t := Tag{"tag"}

b, _ := json.Marshal(t)

fmt.Println(string(b))

// 輸出

{"name":"tag"}

使用反引號包裹標籤,標籤的規則要看處理方法或函式是如何定義的。如何獲取標籤,這裡不做講解。

型別別名

這個和自定義型別是不一樣的,型別別名和原型別是完全等價的,不需要型別轉化,只是名稱不一樣而已。


type byte = uint8

在內建型別中,byte 型別就是 uint8 型別的別名。

總結

本篇講解了如何自定義型別,並且對結構體詳細的展開說明,千萬要掌握,但還沒有講完,下篇講解自定義型別如何攜帶方法。

當你學習過物件導向的語言知道了類的概念後,下來我將類和結構體的相似之處對比下。

  • 類的屬性 —- 結構體欄位

  • 類的方法 —- 結構體方法(下篇講解)

  • 類的繼承 —- 結構體巢狀

這也是在改造物件導向的語言時,Go 語言的結構體被作為類的替代。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章