1. 程式人生 > 其它 >Go通關06:struct和interface,結構體和介面的使用

Go通關06:struct和interface,結構體和介面的使用

結構體

定義

結構體是種聚合型別,裡面可以包含任意型別的值,這些值就是結構體的成員,或成為欄位,定義結構體,需要使用 type+struct 關鍵字組合

type person struct { //人結構體
  name string //人的名字
  age uint //人的年齡
}
  1. type 與 struct 是關鍵字,用來定義一個新結構體的型別。
  2. person 為結構體名字。
  3. name/age 為結構體的欄位名,後面指對應的欄位型別。
  • 欄位宣告和變數類似,變數名在前,型別在後
  • 欄位可以是人一個,一個欄位都沒有的結構體,成為空結構體。
  • 結構體也是一種型別,比如 person 結構體和 person 型別是一個意思。

宣告

  1. 像普通字串、整型醫院宣告初始化
    var p person

聲明瞭一個person型別的變數p,但是沒有初始化,所以預設使用結構體裡欄位的零值。

  1. 字面量方式初始化
    p := person{"無塵",18}

表示結構體變數 p 的name欄位初始化為“無塵”,age欄位初始化為18。順序必須和欄位定義順序一致。

  1. 根據欄位名稱初始化
    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的引數。

值接受者、指標接受者

  1. 實現一個介面,必須實現介面中所有的方法。
  2. 定義一個方法,有值型別接收者和指標型別接收者,兩者都可以呼叫方法,因為Go編譯器自動做了轉換。
  3. 但是介面的實現,值型別接收者和指標型別接收者不一樣

上面介面體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型別實現了該介面。