7.接口
接口多个方法声明集合,代表一种调用契约。
目标类型方法包含接口声明的全部方法,就视为实现该接口,无需显示声明。
接口:不能有字段
只能声明方法,不能实现
可嵌入其他接口
1.
//匿名接口的使用 type N struct{} func (N) test() {} // ------------------------- type node struct { value interface { test() } } // ------------------------- func main() { var t interface { test() } = N{} n := node{ value: t } n.value.test() }
2.空接口
空接口可被赋值任何对象
type Xer interface { toString() string } // ------------------------- type N struct { x int } func (n N) toString() string { return strconv.Itoa(n.x) } // ------------------------- func main() { n := N{ 100 } var x Xer = n // copy n的副本被创建并且赋值给x,x与n是独立的!!! n.x = 200 println(n.toString()) // 200 println(x.toString()) // 100 }
3.匿名嵌入
无法循环嵌入
签名(函数名称,接受阐述,返回参数相同)要相同,golang不可重载(不可以有相同名称,但不同参数的函数
type Aer interface { Test() } type Ber interface { ToString(string) string } // ------------------------- type Xer interface { Aer Ber // 嵌入接口有相同声明。 ToString(s string) string // 签名相同(并集去重)。 // ToString() string // 不允许重载(签名不同)。 // ~~~~~~ duplicate method Print() } // ------------------------- type N struct{} func (N) ToString(string) string { return "" } func (N) Test() {} func (N) Print() {} // ------------------------- func main() { i := Xer(N{}) t := reflect.TypeOf(i) for i := 0; i < t.NumMethod(); i++ { fmt.Println(t.Method(i)) } } /* Print: func(main.N) Test: func(main.N) ToString: func(main.N, string) string */
4.类型转换
超集接口可隐式转换为子集(反之不行
雨痕大佬的代码:将Xer转换为Aer,反之则不行
type Aer interface { toString(string) string } // ------------------------- type Xer interface { // Aer test() toString(string) string } // ------------------------- type N struct{} func (*N) test() {} func (N) toString(s string) string { return s } // ------------------------- func main() { var x Xer = &N{} // super var a Aer = x // sub a.toString("abc") // var x2 Xer = a // ~ Aer does not implement Xer (missing test method) // x2 := Xer(a) // ~ Aer does not implement Xer }
如何判断接口的原始类型
将接口还原为原始类型
func main() { var x Xer = &N{} // super var a Aer = x // sub // 原始类型。 n, ok := a.(*N) fmt.Println(n, ok) // true // 接口。 x2, ok := a.(Xer) fmt.Println(x2, ok) // true }
空接口的还原
func main() { var e any = (*int)(nil) // !!! _, ok := e.(*int) println(ok) // true }
可以利用switch在多种类型中进行还原,如此空接口有更多发挥空间
func main() { var i any = &N{} switch v := i.(type) { case nil: case *int: case func()string: case *N: fmt.Println(v) case Xer: fmt.Println(v) default: } }
无法使用fallthrough(fallthrough
是 Go 编程语言中的一个关键字,用于 switch
语句中。当在一个 case
中使用 fallthrough
时,它会导致控制流传递到下一个 case
标签,无论该 case
的条件是真还是假。)
var i any = &N{} switch v := i.(type) { // v declared but not used case *N: fallthrough // fallthrough statement out of place default: } }
5.接口比较
可以作==,!=的运算
(引用类型无法比较
比较的时候,相同的实现相等,超集与子集相等(
当一个接口的itab(一种内部数据结构,用于支持接口类型的运行时类型信息,记录了一个接口类型和一个具体类型之间的关联信息)与data都为nil,接口为nil
7.1接口的实现
// runtime/runtime2.go type iface struct { tab *itab // 类型和方法表。 data unsafe.Pointer // 目标对象指针。 } type itab struct { inter *interfacetype // 接口类型。 _type *_type // 目标类型。 hash uint32 fun [1]uintptr // 方法表。 offset: 24, 0x18 }
利用方法表进行映射,由接口的函数映射到实现的函数
7.2trick
让编译器检查,确保类型实现了指定接口
利用实现接口的方式将函数转换为接口类型,如何使用接口来实现多态
type FuncString func() string func (f FuncString) String() string { return f() } // ---------------------------------- func main() { f := func() string { return "hello, world!" } var t fmt.Stringer = FuncString(f) fmt.Println(t) }
(因为 string实现了String方法)