Go語(yǔ)言學(xué)習(xí)筆記 - 接口

接口定義

接口是一個(gè)或多個(gè)方法簽名集合,任何類型的方法集中只要擁有與之對(duì)應(yīng)的全部方法,就表示它“實(shí)現(xiàn)”了該接口,無(wú)須在該類型上顯示添加接口聲明。
  所謂對(duì)應(yīng)方法,是指有相同名稱、參數(shù)列表(不包含參數(shù)名)以及返回值。發(fā)然,該類型還可以有其他方法。

  • 接口命名習(xí)慣以er結(jié)尾,結(jié)構(gòu)體。
  • 接口只有方法簽名,沒(méi)有實(shí)現(xiàn)。
  • 接口沒(méi)有數(shù)據(jù)字段。
  • 可在接口中嵌入其他接口。
  • 類型可以實(shí)現(xiàn)多個(gè)接口。
      空接口interface{}沒(méi)有任何方法簽名,也就意味著任何類型都實(shí)現(xiàn)了空接口。其作用類似面向?qū)ο笳Z(yǔ)言中的根對(duì)象object。
func Print(v interface{}) {
  fmt.Printf("%T: %v\n", v, v)
}
func main() {
  Print(1)
  Print("Hello, World!")
}
//輸出:
int: 1
string: Hello, World!

匿名接口可用用作變量類型,或結(jié)構(gòu)成員。

type Tester struct {
  s interface {
    String() string
  }
}
type User struct {
  id  int
  name string
}
func (self *User) String() string {
  return fmt.Sprintf("user %d, %s", self.id, self.name)
}
func main() {
  t := Tester{&User{1, "Tom"}}
  fmt.Println(t.s.String())
}
//輸出:
user 1, Tom

執(zhí)行機(jī)制

接口對(duì)象由接口表 (interface table) 指針和數(shù)據(jù)指針組成。
runtime.h

struct Iface
{
  Itab* tab;
  void* data;
};
struct Itab
{
  InterfaceType* inter;
  Type* type;
  void (*fun[])(void);
};

接口表存儲(chǔ)元數(shù)據(jù)信息,包括接口類型、動(dòng)態(tài)類型,以及實(shí)現(xiàn)接口的方法指針。無(wú)論是反射還是通過(guò)接口調(diào)用用方法,都會(huì)用到這些信息。
  數(shù)據(jù)指針持有的是目標(biāo)對(duì)象的只讀復(fù)制品,復(fù)制完整對(duì)象或指針。

type User struct {
  id int
  name string
}
func main() {
  u := User{1, "Tom"}
  var i interface{} = u
  u.id = 2
  u.name = "Jack"
  fmt.Printf("%v\n", u)
  fmt.Printf("%v\n", i.(User))
}
//輸出:
{2 Jack}
{1 Tom}

接口轉(zhuǎn)型返回臨時(shí)對(duì)象,只有使用指針才能修改其狀態(tài)。

type User struct {
  id int
  name string
}
func main() {
  u := User{1, "Tom"}
  var vi, pi interface{} = u, &u
  // vi.(User).name = "Jack" // Error: cannot assign to vi.(User).name
  pi.(*User).name = "Jack"
  fmt.Printf("%v\n", vi.(User))
  fmt.Printf("%v\n", pi.(*User))
}
//輸出:
{1 Tom}
&{1 Jack}

接口轉(zhuǎn)換

利用類型推斷,可判斷接口對(duì)象是否是某個(gè)具體的接口或類型。還可用用 switch 做批量類型判斷,不支持 fallthrough。

func main() {
  var o interface{} = &User{1, "Tom"}
  switch v := o.(type) {
  case nil:// o == nil
    fmt.Println("nil")
  case fmt.Stringer:// interface
    fmt.Println(v)
  case func() string:// func
    fmt.Println(v())
  case *User:// *struct
    fmt.Printf("%d, %s\n", v.id, v.name)
  default:
    fmt.Println("unknown")
  }
}

超集接口對(duì)象可轉(zhuǎn)換為子集接口,反之出錯(cuò)。

接口技巧

讓編譯器檢查,以確保某個(gè)類型實(shí)現(xiàn)接口。

var _ fmt.Stringer = (*Data)(nil)

某些時(shí)候,讓函數(shù)直接 "實(shí)現(xiàn)" 接口能省不少事。

type Tester interface {
  Do()
}
type FuncDo func()
func (self FuncDo) Do() { self() }
func main() {
  var t Tester = FuncDo(func() { println("Hello, World!") })
  t.Do()
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容