上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
7. 接口
接口(interface)是多个方法声明集合,代表一种调用契约。
只要目标类型方法集包含接口声明的全部方法,就视为实现该接口,无需显示声明。当然,单个目标类型可实现多个接口。在设计上,接口解除了显式类型依赖(DIP,依赖倒置),提供面向对象多态性。应该定义小型、灵活及组合性接口(ISP,接口隔离),减少可视方法,屏蔽对象内部结构和实现细节。
- 不能有字段。
- 只能声明方法,不能实现。
- 可嵌入其他接口。
- 通常以
er
作为名称后缀。 - 空接口(
interface{}
,any
)没有任何方法声明。
接口实现的依据是方法集,所以要区分 T.set
和 *T.set
。
type Xer interface { test() toString() string } // ------------------------- type N struct{} func (*N) test() {} func (N) toString() string { return "" } // ------------------------- func main() { var n N // var t Xer = n // ~ N does not implement Xer // test method has pointer receiver var t Xer = &n t.test() t.toString() }
匿名接口可直接用于变量定义,或作为结构字段类型。
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() }
空接口可被赋值任何对象。
type any = interface{} // 别名
func main() { var i any = 123 fmt.Println(i) i = "abc" fmt.Println(i) }
注意,接口会复制目标对象,通常以指针替代原始值。
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 = 200 println(n.toString()) // 200 println(x.toString()) // 100 }
匿名嵌入
像匿名字段那样,嵌入其他接口。目标类型方法集中,必须包含嵌入接口在内的全部方法实现。
- 相当于导入(include)声明,非继承。
- 不能嵌入自身或循环嵌入。
- 不允许声明重载(overload)。
- 允许签名相同的声明(并集去重)。
- 鼓励小接口嵌入组合。
签名包括方法名、参数列表(数量、类型、排列顺序)和返回值,不包括参数名。
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 */
类型转换
超集接口(即便非嵌入)可隐式转换为子集,反之不行。
- 超集包含子集的全部声明(含嵌入)。
- 和声明顺序无关。
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
语句在多种类型间做出推断匹配,如此空接口就有更多发挥空间。
- 未使用变量视为错误。
- 不支持
fallthrough
。
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: } }
func main() { var i any = &N{} switch v := i.(type) { // v declared but not used case *N: fallthrough // fallthrough statement out of place default: } }
接口比较
如实现接口的动态类型支持,可做相等( ==
、 !=
)运算。
func main() { println(any(nil) == any(nil)) println(any(100) == any(100)) // println(any(map[string]int{}) == any(map[string]int{})) // ^ comparing uncomparable type map[string]int }
内部实现
runtime.efaceeq
、runtime.ifaceeq
直接比较.data
。
允许方式:
- 接口类型相同。
- 接口类型不同,但声明完全相同。
- 接口类型是超集或子集。
type Aer interface { test() } type Ber interface { test() } type Cer interface { toString() string } type Xer interface { test() toString() string } // ------------------------- type N struct { x int } func (N) test() {} func (N) toString() string { return "" } // ------------------------- func main() { a, b := N{ 100 }, N{ 100 } println(a == b) // true // 相同类型接口。 println(Aer(a) == Aer(b)) // true println(Aer(&a) == Aer(&b)) // false // 不同接口类型,但声明相同(或超集)。 println(Aer(a) == Ber(b)) // true println(Aer(a) == Xer(b)) // true // 不同接口类型,声明不同。 // println(Aer(a) == Cer(b)) // mismatched types Aer and Cer // 对象和它实现的接口类型(b impl Aer)。 println(Aer(a) == b) // true }
接口内部由两个字段组成: itab
和 data
。
只有两字段都为 nil
时,接口才等于 nil
。可利用反射完善判断结果。
不能直接以指针判断,因为编译器可能将其指向
runtime.zerobase
或zeroVal
全局变量。
func main() { var n *N var x Xer = n // type != nil println(x == nil) // false println(x == nil || reflect.ValueOf(x).IsNil()) // true }
func main() { var t1 any // type == nil var t2 any = ([]int)(nil) // type != nil println(t1 == nil) // true println(t2 == nil) // false println(t1 == nil || reflect.ValueOf(t1).IsNil()) // true println(t2 == nil || reflect.ValueOf(t2).IsNil()) // true }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论