1.接口定义
接口是一个或多个方法签名的集合,任何类型的“方法集”中只要拥有与之对应的全部方法,
就表示它 "实现" 了该接口,无须在该类型上显式添加接口声明。
所谓对应方法,是指有相同名称、参数列表 (不包括参数名) 以及返回值。当然,该类型还
可以有其他方法。
• 接口命名习惯以 er 结尾,结构体。
• 接口只有方法签名,没有实现。
• 接口没有数据字段。
• 可在接口中嵌入其他接口。
• 类型可实现多个接口。
package main
import "fmt"
type Stringer interface{
String() string
}
type Printer interface{
Stringer //接口嵌入
Print()
}
type User struct{
id int
name string
}
func (self *User) String() string{
return fmt.Sprintf("user %d, %s",self.id,self.name)
}
func (self *User) Print() {
fmt.Println(self.String())
}
func main() {
var t Printer=&User{1,"Feng Wei"}
t.Print()
}
输出:
user 1, Feng Wei
空接口 interface{} 没有任何方法签名,也就意味着任何类型都实现了空接口
package main
import "fmt"
func Print(v interface{}) {
fmt.Printf("%T: %v\n",v,v)
}
func main() {
Print(1)
Print("Hello Feng Wei!")
}
输出:
int: 1
string: Hello Feng Wei!
2.执行机制
接口对象由接口表指针和数据指针构成,数据指针持有的是目标对象的只读复制品,复制完整对象或指针。
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}
接口转型返回临时对象,只有使用指针才能修改其状态。
package main
import "fmt"
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}
只有 tab 和 data 都为 nil 时,接口才等于 nil。
package main
import (
"fmt"
"unsafe"
"reflect"
)
func main() {
var a interface{} = nil // tab = nil, data = nil
var b interface{} = (*int)(nil) // tab 包含 *int 类型信息, data = nil
type iface struct {
itab, data uintptr
}
ia := *(*iface)(unsafe.Pointer(&a))
ib := *(*iface)(unsafe.Pointer(&b))
fmt.Println(a == nil, ia)
fmt.Println(b == nil, ib, reflect.ValueOf(b).IsNil())
}
true {0 0}
false {4792000 0} true
3.接口转换
利用类型推断,可判断接口对象是否某个具体的接口或类型。
package main
import "fmt"
type Stringer interface{
String() string
}
type Printer interface{
Stringer //接口嵌入
Print()
}
func (self *User) String() string{
return fmt.Sprintf("user %d, %s",self.id,self.name)
}
func (self *User) Print() {
fmt.Println(self.String())
}
type User struct {
id int
name string
}
func main() {
var o interface{} = &User{1, "Tom"}
if i, ok := o.(fmt.Stringer); ok { // ok-idiom
fmt.Println(i)
}
u := o.(*User)
// u := o.(User) // panic: interface is *main.User, not main.User
fmt.Println(u)
}
输出:
user 1, Tom
user 1, Tom
4.接口技巧
让编译器检查,以确保某个类型实现接口。
var _ fmt.Stringer = (*Data)(nil)
某些时候,让函数直接 "实现" 接口能省不少事。
type Tester interface {
Do()
}
type FuncDo func()
func (self FuncDo) Do() { self() }
func main() {
var t Tester = FuncDo(func() { println("Hello, World!") })
t.Do()
}