Go通關06:struct和interface,結構體和介面的使用
結構體
定義
結構體是種聚合型別,裡面可以包含任意型別的值,這些值就是結構體的成員,或成為欄位,定義結構體,需要使用 type+struct 關鍵字組合
type person struct { //人結構體
name string //人的名字
age uint //人的年齡
}
- type 與 struct 是關鍵字,用來定義一個新結構體的型別。
- person 為結構體名字。
- name/age 為結構體的欄位名,後面指對應的欄位型別。
- 欄位宣告和變數類似,變數名在前,型別在後
- 欄位可以是人一個,一個欄位都沒有的結構體,成為空結構體。
- 結構體也是一種型別,比如 person 結構體和 person 型別是一個意思。
宣告
- 像普通字串、整型醫院宣告初始化
var p person
聲明瞭一個person型別的變數p,但是沒有初始化,所以預設使用結構體裡欄位的零值。
- 字面量方式初始化
p := person{"無塵",18}
表示結構體變數 p 的name欄位初始化為“無塵”,age欄位初始化為18。順序必須和欄位定義順序一致。
- 根據欄位名稱初始化
p := person{age:18,name:"無塵"}
像這樣指出欄位名,就可以打亂初始化欄位的順序。也可以只初始化其中部分欄位,剩餘欄位預設使用零值:
p := person{age:30}
欄位結構體
結構體欄位可以是任意型別,包括自定義的結構體型別:
type person struct { //人結構體
name string
age uint
addr address //使用自定義結構體型別
}
type address struct { //地址結構體
city string
}
對於這樣巢狀結構體,初始化和一般結構體類似,根據欄位對應的型別初始化即可:
p := person {
age:18,
name:"無塵",
addr:address{
city:"北京",
},
}
結構體的欄位和呼叫一個型別的方法一樣,都是使用點操作符“.”:
fmt.Println(p.age) //訪問巢狀結構體裡的city欄位的值: fmt.Println(p.addr.city)
介面
定義
介面是一個抽象的型別,是和呼叫方的一種約定。介面只需要定義約定,告訴掉用方可以做什麼,而不用知道它的內部實現。
介面的定義是 type + interface關鍵字類實現。
//Info 是一個介面,它有方法 Getinfo()string
type Info interface {
Getinfo() string
}
對應 Stringer 介面,它會告訴呼叫者可以通過 String()放獲取一個字串,這就是介面的約定,而這個字串是怎麼獲取到的,介面並不關心,呼叫者也不用關心,因為這些是介面的實現者來處理的。
介面的實現
介面的實現者必須是一個具體的型別:
func (p person) Getinfo() string {
return fmt.Sprintf("my name is %s,age is %d",p.name,p.age)
}
- 給結構體型別 person 定義了一個方法,這個方法和接口裡的方法名稱、引數、返回值都一樣,就表示這個結構體 person 實現了 Info 介面。
- 如果一個介面有多個方法,那麼要實現介面中的所有方法才算是實現了這個介面。
使用
我們先定義一個可以列印 Info 介面的函式:
func printInfo(i Info) {
fmt.Println(i.Getinfo())
}
- 定義函式 pringInfo,它接收一個 Info 介面型別的引數,然後列印介面 Getinfo 方法返回的字串。
- 這個 pringInfo 函式此處是面向介面程式設計,只有任何一個型別實現了Info介面,都可以使用這個函式打印出對應的字串,而不用關心具體的型別實現。
printInfo(p)
//結果為:my name is 無塵,age is 18
因為 person 型別實現了Info介面,所以變數p可以作為函式printInfo的引數。
值接受者、指標接受者
- 實現一個介面,必須實現介面中所有的方法。
- 定義一個方法,有值型別接收者和指標型別接收者,兩者都可以呼叫方法,因為Go編譯器自動做了轉換。
- 但是介面的實現,值型別接收者和指標型別接收者不一樣
上面介面體person實現了Info介面,是否結構體指標也實現了該介面呢?
printInfo(&p)
測試發現p的指標作為引數函式也是可以正常執行,表明以值型別接收者實現介面,型別本身和該型別的指標型別,都實現了該介面
那麼把接收者改成指標型別:
func (p *person) Getinfo() string {
return fmt.Sprintf("my name is %s,age is %d",p.name,p.age)
}
然後再呼叫函式 printInfo(p)
,程式碼編譯不通過,表明以指標型別接收者實現介面,只有對應的指標型別才被認為實現了介面
方法接收者 | 實現介面型別 |
---|---|
(p person) | person和*person |
(p *person) | *person |
- 當值型別作為接收者,person型別和*person型別都實現了該介面。
- 當指標型別作為接收者,只有 *person型別實現了該介面。