var a1 animal a1 = dog{} a1.Sing() a1 = &dog{} a1.Sing() //a1 = cat{} // cannot use cat{} (value of type cat) as type animal in assignment: //a1.Sing()
空接口
空接口本身
1 2
var y interface{} fmt.Printf("%T %v\n", y, y)
输出
1
<nil> <nil>
空接口可以接受任意类型变量或者函数。
1 2 3 4 5 6 7
var x interface{} x = "A" fmt.Printf("%T %v\n", x, x) x = 123 fmt.Printf("%T %v\n", x, x) x = func() {} fmt.Printf("%T %v\n", x, x)
var x interface{} x = "abc" if i, ok := x.(string); ok { fmt.Printf("%s", i) }
1 2 3 4 5 6 7 8
var x interface{} x = 123 switch x.(type) { // 此处x一定是一个interface类型,如果去掉x类型声明(var x interface{}),则会报错 case int: fmt.Printf("%T %d\n", x, x) case string: fmt.Printf("%T %s\n", x, x) }
接口和nil的比较
只有类型和值都为nil时,这个接口才为nil
1 2
var x interface{} = nil fmt.Println(x == nil) // true
当一个接口显式的赋值为nil,此时这个接口的 type 和data 都等于 nil,接口 == nil
1 2 3 4 5 6 7 8 9 10 11 12 13
type name interface { Name() }
type people struct{}
func(p *people) Name() {}
funcmain() { var x name = &people{} fmt.Printf("%T %v\n", x, x) // *main.people &{} fmt.Println(x == nil) // false }
func (p Pig) walk() { fmt.Printf("%s is walking\n", p.name) }
func (b Bird) walk() { fmt.Printf("%s is walking\n", b.name) }
func (b Bird) fly() { fmt.Printf("%s is flying\n", b.name) }
func main() { var w, f interface{} // w 和 f 都是接口 w = Pig{name: "pig"} f = Bird{name: "bird"} s := []interface{}{w, f} for _, v := range s { if b, ok := v.(Pig); ok { // 接口断言类型 b.walk() } if p, ok := v.(Bird); ok { p.fly() p.walk() } } }
执行结果
1 2 3
pig is walking bird is flying bird is walking
类型断言接口
1 2 3 4 5 6 7 8 9 10 11 12
var p = Pig{name: "pig"} var b = Bird{name: "bird"} s := []interface{}{p, b} for _, v := range s { if f, ok := v.(Flyer); ok { f.fly() }
if f, ok := v.(Walker); ok { f.walk() } }
输出
1 2 3
pig is walking bird is flying bird is walking
判断是否实现接口
在第三方包中,通常会有这样的代码
1
var _ I = T
是用来验证T类型是否实现了I方法
例如
1 2 3 4 5 6 7 8 9
type name interface { Name() }
type people struct { string }
func (p *people) Name() {}
1 2 3
var _ name = people{} // 编译器报错:cannot use people{} (value of type people) as type name in variable declaration: people does not implement name (Name method has pointer receiver)
运行时报错:panic: runtime error: comparing uncomparable type []int
map
1
运行时报错:panic: runtime error: comparing uncomparable type map[int]int
channel
1 2 3 4 5
可以比较,比较的是内存地址 var a interface{} = make(chan int) var b interface{} = make(chan int) fmt.Printf("%v %v\n", a, b) // 0x1400010e060 0x1400010e0c0 fmt.Println(a == b) // false
函数底层是指针,但是不能比较,运行时报错 var a interface{} = func() {} var b interface{} = func() {} fmt.Printf("%v %v\n", a, b) fmt.Println(a == b) // panic: runtime error: comparing uncomparable type func()
接口底层内部实现
接口的底层代码在src/runtime/runtime2.go中
1 2 3 4 5 6 7 8 9 10 11
// 非空接口 type iface struct { tab *itab data unsafe.Pointer }
// 空接口 type eface struct { _type *_type data unsafe.Pointer }
非空接口初始化的过程是初始化一个iface类型的结构
1 2 3 4
type iface struct { tab *itab // 存放接口自身类型及方法指针信息 data unsafe.Pointer // 数据信息 }
iface的结构有两个指针字段
1 2
itab 用来存放接口自身类型,和绑定的实例类型,以及实例相关的函数指针 data 指向接口绑定的实力的副本,接口的初始化也是一种值拷贝
type itab struct { inter *interfacetype // 接口类型元信息的指针 _type *_type // 指向接口存放的具体类型元信息的指针,data指针指向的是该类型的值 hash uint32 // copy of _type.hash. Used for type switches. // 拷贝的_type.hash.,是类型的hash值,用于接口断言或类型查询快速访问 _ [4]byte fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter. // 指向具体类型的方法 }
type _type struct { size uintptr // 大小 ptrdata uintptr // size of memory prefix holding all pointers hash uint32 // hash tflag tflag // 类型的特征标记 align uint8 // _type作为整体交量存放时的对齐字节数 fieldAlign uint8 // 当前结构体字段的对齐字节数 kind uint8 // 基础类型枚举值和反射中的Kind一致,kind决定了如何解析该类型,例如bool,string等 // function for comparing objects of this type // (ptr to object A, ptr to object B) -> ==? equal func(unsafe.Pointer, unsafe.Pointer) bool // 比较两个类型是否相等的函数 // gcdata stores the GC type data for the garbage collector. // If the KindGCProg bit is set in kind, gcdata is a GC program. // Otherwise it is a ptrmask bitmap. See mbitmap.go for details. gcdata *byte // GC相关信息 str nameOff ptrToThis typeOff }