接口是Go类型语言中数据类型的关键。接口是一个或多个方法签名的集合,对于任何类型,只要拥有以这个接口对应的全部方法,就说明该类型实现了这个接口,无需在该类型上显示添加接口声明。
对应的方法是指具有相同的名称,参数列表,以及返回值。除此之外,该类型还可以有其他。
说一下接口的特点:
- 接口命名一般习惯以er结尾,结构体;
- 接口只有方法的签名,并非是实现了某方法
- 接口没有数据字段
- 在接口中可以嵌套其他接口
- 类型可以实现多个接口
type User struct { id int name string } func (u *User) TellId() { fmt.Println(u.id) } func (u *User) String() string { return fmt.Sprintf("user:%d", u.id) } func (u *User) TellName() { fmt.Println(u.name) } type Stringer interface { String() string } type Printer interface { TellId() } type UUer interface { Printer TellName() } func main() { var user = &User{10, "goland"} var user1 Printer = &User{1, "tom"} var user2 Stringer = &User{2, "jason"} var user3 UUer = &User{3, "jack ma"} user1.TellId() // 1 fmt.Println(user2.String()) // user:2 user3.TellId() // 3 //fmt.Println(user3.String()) // user3.String undefined (type UUer has no field or method String) fmt.Println(user.String()) // user:10 user.TellId() // 10 user.TellName() // goland //userToUUer := UUer(user) //userToUUer.String() // userToUUer.String undefined (type UUer has no field or method String) }
从上述栗子中可以看出,User实现了Stringer, Printer, UUer的所有接口,因此user的实例可以调用所有的方法,并且同时可以看出,接口是可以嵌套的
另外可以看出超集接口可以转化为子集接口,反过来去不行,一旦转化为子集接口,则该类型只可以使用子集方法。
空接口interfacr{} 没有任何方法和签名,这也就意味着任何类型都实现了空接口。
我们可以利用interface进行接口类型判断,注意这个里面是不支持fallthrough。
type User struct { id int name string } func main() { var m interface{} = &User{1, "jack ma"} switch v := m.(type) { case nil: fmt.Println("nil") case fmt.Stringer: // 因为User没有实现String()string方法,因为没实现这个接口 fmt.Println("string", v) case func() string: fmt.Println("func") case *User: fmt.Println("User") default: fmt.Println("i do not know") } }// 最终打印User
关于执行机制,接口对象由接口表(interface table)指针和数据指针组成,部分源码文件如下,根据 interface 是否含有方法,低层实现用了下述两个 struct 来进行表示, iface代表non-empty,这个里面包含实现的方法, eface代表是empty,就是里面不包含方法,这个编译器会自动帮助我们选择。从这里面可以看到,interface实际上有两个成员,tab和data,tab指向的是虚表,data则指向实际引用数据
type iface struct { tab *itab data unsafe.Pointer } type eface struct { _type *_type data unsafe.Pointer }
经过上述分析,我们就可以判定interface与nil的关系
type State struct{} func testnil1(a, b interface{}) bool { return a == b } func testnil2(a *State, b interface{}) bool { return a == b } func testnil3(a interface{}) bool { return a == nil } func testnil4(a *State) bool { return a == nil } func testnil5(a interface{}) bool { v := reflect.ValueOf(a) return !v.IsValid() || v.IsNil() } func main() { var a *State fmt.Println(testnil1(a, nil)) // false fmt.Println(testnil2(a, nil)) // false fmt.Println(testnil3(a)) //false fmt.Println(testnil4(a)) // true fmt.Println(testnil5(a)) // true }
interface{}一共包含两个指针,一个指向值的类型,另外一个指向值的类型,对一个interface{}类型的nil变量来说,它的两个指针都是0;但是var a *State传进去后,指向的类型的指针不为0了,因为有类型了, 所以比较为false。 interface 类型比较, 要是两个指针都相等, 才能相等。