Golang基础进阶——interface
go语言中,接口 (interface) 是一个自定义类型,描述了一系列方法的集合,声明格式:
type 接口类型名称 interface{ 方法名1(参数列表1) 返回值1 方法名2(参数列表2) 返回值2 }
Go语言再接口命名时,一般会在单词后面添加er,如有写操作的接口叫 Writer 。
接口
接口的基本用法
IO包中提供的Writer接口
type Writer interface { Write(p []byte) (n int, err error) }
这个接口可以调用 Writer() 方法写入一个字节数组 ([]byte) ,返回值告知写入字节数 (n int) 和可能发生的错误 (err error) 。
fmt包中的String()接口
type Stringer interface { String() string }
自定义示例
type Humaner interface { Say() } type Student struct { name string score int } func (s Student) Say() { fmt.Printf("Student[%s,%d]瞌睡不断 ", s.name, s.score) } type Teacher struct { name string group string } func (t Teacher) Say() { fmt.Printf("Teacher[%s,%s] 毁人不倦 ", t.name, t.group) } type MyStr string func (str MyStr) Say() { fmt.Printf("MyStr[%s] 你你你你! ", str) } func WhoSay(i Humaner) { i.Say() } func main() { s := Student{"szs", 88} t := Teacher{"lis", "666"} var tmp MyStr = "333" s.Say() t.Say() tmp.Say() fmt.Println() WhoSay(s) WhoSay(t) WhoSay(tmp) fmt.Println() x := make([]Humaner, 3) x[0], x[1], x[2] = s, t, tmp for _, value := range x { value.Say() } }
接口继承
type Humaner interface { Say() } type Personer interface { Humaner Sing(lyrics string) } type Student struct { name string score int } func (s *Student) Say() { fmt.Printf("Student[%s,%d]瞌睡不断 ", s.name, s.score) } func (s *Student) Sing(lyrics string) { fmt.Printf("Student sing[%s] ", lyrics) } func main() { s := &Student{"zs", 88} var p Personer p = s p.Say() p.Sing("葫芦娃") }
接口类型变量
type DataWriter interface { WriteData(data interface{}) error } type file struct { } func (d *file) WriteData(data interface{}) error { fmt.Println("writeData:", data) return nil } func main() { f := new(file) var writer DataWriter writer = f writer.WriteData("data") }
接口值类型
type Animal interface { Talk() Eat() Name() string } type Dog struct { name int } func (d Dog) Talk() { fmt.Println("嘤嘤嘤") } func (d Dog) Eat() { fmt.Println("吃") } func (d Dog) Name() string { fmt.Println("名字:旺财") return "旺财" } func main() { var a Animal var d *Dog = &Dog{} a = d a.Eat() }
接口指针类型
type Animal interface { Talk() Eat() Name() string } type Dog struct { name int } func (d *Dog) Talk() { fmt.Println("嘤嘤嘤") } func (d *Dog) Eat() { fmt.Println("吃") } func (d *Dog) Name() string { fmt.Println("名字:旺财") return "旺财" } func main() { var a Animal var d = Dog{1} a = d // 报错 }
一个类型实现多个接口
Socket 包实现了 IO 包的 Writer 和 Closer 接口
type Writer interface { Write(p []byte) (n int, err error) } type Closer interface { Close() error }
Socket 中实现
type Socket struct{ } func (s *Socket) Write(p []byte) (n int, err error){ return 0, nil } func (s *Socket) Close()error { return nil }
自定义示例
type Animal interface { Talk() Eat() Name() string } type Animal2 interface { Run() } type Dog struct { name int } func (d *Dog) Run() { fmt.Println("狗子,奔跑") } func (d *Dog) Talk() { fmt.Println("嘤嘤嘤") } func (d *Dog) Eat() { fmt.Println("吃") } func (d *Dog) Name() string { fmt.Println("名字:旺财") return "旺财" } func main() { var a Animal var d Dog a = &d a.Eat() var a2 Animal2 a2 = &d a2.Run() }
接口嵌套
IO包中定义
type Writer interface { Write(p []byte) (n int, err error) } type Closer interface { Close() error } type WriteCloser interface { Writer Closer }
使用接口嵌套组合
type device struct { } func (d *device) Write(p []byte) (n int, err error) { return 0, nil } func (d *device) Close() error { return nil } func main() { var wc io.WriteCloser = new(device) wc.Write(nil) wc.Close() var writeOnly io.Writer = new(device) writeOnly.Write(nil) }
接口类型断言
Go语言中使用接口断言 ( type assertions ) 将接口转换成另外一个接口,也可以将接口转换为另外的类型。接口的转换在开发中非常常见, 使用也非常频繁。
类型断言的格式
t := i.(T)
- i代表接口变量
- T代表转换的目标类型
- t代表转换后的变量
如果 i 没有实现 T接口的方法,此写法容易触发宕机,可换成另外一种写法:
t,ok := i.(T)
这种写法下,如果发生接口未实现时,将会把ok置为false,t置为T类型的0值。正常实现时,ok为true。这里ok可以被认为是:i接口是否实现T类型的结果
示例1
type Element interface { } type Person struct { name string age int } func main(){ list := make([]Element, 3) list[0] = 1 list[1] = "hello" list[2] = Person{"zs", 18} for index, element := range list { // 类型断言: value,ok := 元素.(Type) // value是变量值,ok是布尔,是不是这个类型 if value, ok := element.(int); ok { fmt.Printf("list[%d] 是int类型,值是 %d ", index, value) } else if value, ok := element.(int); ok { fmt.Printf("list[%d] 是int类型,值是 %d ", index, value) } else if value, ok := element.(Person); ok { fmt.Printf("list[%d] 是Person类型,值[%s,%d] ", index, value.name, value.age) } else { fmt.Println("不支持的类型") } } //for index, element := range list { // switch value := element.(type) { // case int: // fmt.Printf("list[%d]是int类型,值是%d ", index, value) // case string: // fmt.Printf("list[%d] 是string类型,值是%s ", index, value) // default: // fmt.Printf("不支持") // } //} }
示例2
type Flyer interface { Fly() } type Walker interface { Walk() } type bird struct { } func (b *bird) Fly() { fmt.Println("bird: fly") } func (b *bird) Walk() { fmt.Println("bird: walk") } type pig struct { } func (p *pig) Walk() { fmt.Println("pig: walk") } func main() { animals := map[string]interface{}{ "bird": new(bird), "pig": new(pig), } for name, obj := range animals { f, isFlyer := obj.(Flyer) w, isWalker := obj.(Walker) fmt.Printf("name:%s isFlyer:%v isWalker:%v ", name, isFlyer, isWalker) if isFlyer { f.Fly() } if isWalker { w.Walk() } } }
示例3
判断基本类型
func printType(v interface{}) { switch v.(type) { case int: fmt.Println(v, "is int") case string: fmt.Println(v, "is string") case bool: fmt.Println(v, "is bool") } } func main() { printType(1024) printType("pig") printType(true) }
判断接口类型
// 电子支付方式 type Alipay struct { } // 为Alipay添加CanUseFaceID方法,表示电子支付方式支持刷脸 func (a *Alipay) CanUseFaceID() { } // 现金支付方式 type Cash struct { } // 为Cash添加Stolen方法,表示现金支付方式会出现偷窃情况 func (a *Cash) Stolen() { } type CantainCanUseFaceID interface { CanUseFaceID() } type ContainStolen interface { Stolen() } // 打印支付方式具备的特点 func print(payMethod interface{}) { switch payMethod.(type) { case CantainCanUseFaceID: // 可以刷脸 fmt.Printf("%T can use faceid ", payMethod) case ContainStolen: // 可能被偷 fmt.Printf("%T may be stolen ", payMethod) } } func main() { // 使用电子支付判断 print(new(Alipay)) // 使用现金判断 print(new(Cash)) }
将接口转换为普通指针
// 参照上面的代码 func main() { p1 := new(pig) var a Walker = p1 p2 := a.(*pig) fmt.Printf("p1=%p p2=%p", p1, p2) }
1. 由于pig实现了 Walker 接口,因此可以被隐式转换为 Walker 接口类型保存于a中。
2. 由于a中本来就是 *pig 本体,因此可以转换为 *pig 类型。
3. 对比发现,p1和p2指针是相同的。
空接口类型
空接口类型可以保存任何值,也可以从空接口中取出原值。使用空接口保存一个数据的过程要比直接用数据对应的类型的变量保存稍慢。
示例
func main() { var any interface{} any = 1 // fmt.Println(any) any = "hello" // fmt.Println(any) any = false fmt.Println(any) }
保存到空接口的值,如果直接取出指定类型的值,会发生错误。
func main() { var a int = 1 var i interface{} = a var b int = i }
使用类型断言可以解决
var b int = i.(int)
空接口的值比较
空接口在保存不同的值后,可以和其他变量值一样使用“==”进行比较操作。
func main() { var a interface{} = 100 var b interface{} = "h1" fmt.Println(a == b) }
不能比较空接口中的动态值
func main() { var c interface{} = []int{10} var d interface{} = []int{20} fmt.Println(c == d) }
使用空接口实现字典
// 字典结构 type Dictionary struct { data map[interface{}]interface{} // 键值都为interface{}类型 } // 根据键获取值 func (d *Dictionary) Get(key interface{}) interface{} { return d.data[key] } // 设置键值 func (d *Dictionary) Set(key interface{}, value interface{}) { d.data[key] = value } // 遍历所有的键值,如果回调返回值为false,停止遍历 func (d *Dictionary) Visit(callback func(k, v interface{}) bool) { if callback == nil { return } for k, v := range d.data { if !callback(k, v) { return } } } // 清空所有的数据 func (d *Dictionary) Clear() { d.data = make(map[interface{}]interface{}) } // 创建一个字典 func NewDictionary() *Dictionary { d := &Dictionary{} // 初始化map d.Clear() return d } func main() { // 创建字典实例 dict := NewDictionary() // 添加游戏数据 dict.Set("My Factory", 60) dict.Set("Terra Craft", 36) dict.Set("Don't Hungry", 24) // 获取值及打印值 favorite := dict.Get("Terra Craft") fmt.Println("favorite:", favorite) // 遍历所有的字典元素 dict.Visit(func(key, value interface{}) bool { // 将值转为int类型,并判断是否大于40 if value.(int) > 40 { // 输出很贵 fmt.Println(key, "is expensive") return true } // 默认都是输出很便宜 fmt.Println(key, "is cheap") return true }) }