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