跳至主要内容

Struct、Interface

介紹

數組和切片是 Go 語言中常見的數據結構,很多剛使用 Go 的開發者往往會混淆這個概念。 切片和哈希表皆是引用類型,基礎數據和數組是值類型。

Struct 宣告方式

type Person struct {
Name string
Age int
sid string // 私有屬性無法被訪問和 json 包序列化
}

p := Person
p.Name = "Cindy"
p.Age = 40

p := Person{
Name: "John",
Age: 10,
}

p := &Person{
Name: "John Smith",
Age: 20,
}

p := new(Person)
p.Name = "Merry"
p.Age = 30

結構體定義方法

type Account struct {
Name: string
Email: string
}

func (a *Account) GetName() string {
return a.Name
}

結構體、JSON互相轉換

type Account struct {
Name: string
Email: string
}

a := &Account{
Name: "Merry",
Email: "merry@gmail.com",
}

jsonBtyeSlice, _ := json.Marshal(a) // []byte{0x7b, 0x22, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x3a, 0x22, 0x4d, 0x65, 0x72, 0x72, 0x79, 0x22, 0x2c, 0x22, 0x41, 0x67, 0x65, 0x22, 0x3a, 0x34, 0x30, 0x7d}
jsonStr := string (jsonBtyeSlice) // &main.Person{Name:"Merry", Age:40}"{\"Name\":\"Merry\",\"Age\":40}"

str := "{\"Name\":\"Merry\",\"Age\":40}"
a := &Account{}

err := json.Unmarshal([]byte(str), a)

Struct 標籤

Tag 是結構體的元訊息,可以在運行的時候通過反射機制讀取出來。Tag 在結構體字段的後方定義,由一對反引號包裹起來,格式如下: key1:"val1" key2:"val2" 結構體 tag 由一個或多個鍵值對組成。鍵與值使用冒號分隔,值用雙引號刮起來。同一個結構體字段可以設置多個鍵值對 tag,不同鍵值對之間用使用空格分隔。

type Account struct {
Name: string `json:"name"`
Email: string `json:"email"`
}

Interface 介面

可以使用 interface 定義物件方法,而當我們有自定義的型態假設想要擁有這些行為,就是去實踐 interface 裡面的方法。interface 類型默認是一個指針 (引用類型),如果沒有對 interface 初始化就使用,那麼會輸出 nil。

var animal AnimalIF
package main

import "fmt"

type AnimalIF interface {
Eat()
Run()
}

type Dog struct {
Name string
}

func (d *Dog) Eat() {
fmt.Printf("%s is eating\n", d.Name)
}

func (d *Dog) Run() {
fmt.Printf("%s is running\n", d.Name)
}

func ShowEat(animal AnimalIF) {
animal.Eat()
}

func ShowRun(animal AnimalIF) {
animal.Run()
}

func main() {
dog := Dog{Name:"Kenny"}
ShowEat(&dog)
ShowRun(&dog)
}

在 Golang 中如果自定義型態實現了 interface 的所有方法,那麼它就會認定該自定義型態也是 interface 型態的一種。也就是所謂的鴨子型別 (Duck typing) 的實現。只要你有符合這些種種的行為,即使你不是真的鴨子,那麼還是會認定你是一隻鴨子。 透過 ShowRun 跟 ShowEat () 得知實現了多型的行為。所謂多型的意思是相同的訊息給予不同的物件會引發不同的動作,因為參數型態用 Animal 所以,每個動物會有各自的吃跟跑的行為,執行出來的結果也會各自不一樣。

型態斷言

假設利用自定義型態為 AnimalIF interface 指定型態的話,該型態就能存取 interface 的行為,並不能存取自定義型態的屬性及其它自定義型態的方法。

透過.(type) 來一一比對出對的型態。此外,Golang 型態斷言也提供了檢測機制:

func main() {
animals := [...]AnimalIF{
&Dog{Name:"Kenny"},
&Cat{Name:"Nicole"},
}

for _, animal := range animals {
if dog, ok := animal.(*Dog); ok {
fmt.Println(dog.Name)
}

if cat, ok := animal.(*Cat); ok {
fmt.Println(cat.Name)
}
}
}

空 interface 的限制

一個空的接口會隱藏值對應的表示方式和所有的公開的方法,必須使用類型斷言才能來來訪問內部的值,如果事先不知道空接口指向的值的具體類型,就無法操作。