接口定義
接口是一個(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()
}