21.接口类型
接口类型是其他类型行为的概括与抽象。接口是一种抽象类型,它并没有暴露了基于这个精确布局的内部操作。
1: type Humaner interface {
2: SayHi()
3: }
接口命令习惯以er结尾
接口只有方法声明,没有实现,没有数据字段
接口可以匿名嵌入其他接口,或嵌入到结构中
接口实现
接口是用来定义行为的类型。这些被定义的行为不由接口直接实现,而是由用户定义的类型实现,一个实现了这些方法的具体类型是这个接口类型的实例。
如果用户定义的类型实现了某个接口类型声明的一组方法,那么这个用户定义的类型的值就可以赋给这个接口类型的值。这个赋值会把用户定义的类型的存入接口类型的值。
代码示例:
1: package main
2:
3: import (
4: "fmt"
5: )
6:
7: type Humaner interface{
8: sayHi()
9: }
10: type Student struct {
11: name string
12: id int
13: }
14:
15: //这样就可以说Student实现了此方法
16: func (a *Student) sayHi() {
17: fmt.Printf("Student(%d, %s) sayhi\n", a.id, a.name)
18: }
19: type Teacher struct {
20: addr string
21: group string
22: }
23: // 同上
24: func (a *Teacher) sayHi() {
25: fmt.Printf("Teacher(%s, %s) sayhi\n", a.addr, a.group)
26: }
27:
28: type Mystr string
29:
30: func (a *Mystr) sayHi() {
31: fmt.Printf("Mystr(%s) sayhi\n", *a)
32: }
33:
34: func main() {
35: //定义接口类型的变量
36: var i Humaner
37: // 只要实现了此接口方法的类型,那么这个类型的变量就可以给i赋值
38: s := &Student{"mike", 666}
39: i = s
40: i.sayHi()
41:
42: t := &Teacher{"bj", "go"}
43: i = t
44: i.sayHi()
45:
46: var str Mystr = "hello mike"
47: i = &str
48: i.sayHi()
49: }
1: >>> Student(666, mike) sayhi
2: Teacher(bj, go) sayhi
3: Mystr(hello mike) sayhi
换一种写法,接上面,更换main函数,结果一样
1: // 定义一个普通函数,函数的参数为接口类型
2: func WhoSayHi(i Human) {
3: i.sayHi()
4: }
5:
6: func main() {
7: s := &Student{"mike", 666}
8: t := &Teacher{"bj", "go"}
9: var str Mystr = "hello mike"
10: // 调用同一个函数,可以有不同表现,多态性的表现
11: WhoSayHi(s)
12: WhoSayHi(t)
13: WhoSayHi(&str)
14: }
也可以用切片
1: func main() {
2: s := &Student{"mike", 666}
3: t := &Teacher{"bj", "go"}
4: var str Mystr = "hello mike"
5:
6: x := make([]Human, 3)
7: x[0] = s
8: x[1] = t
9: x[2] = &str
10:
11: for _, i := range x{
12: i.sayHi()
13: }
14: }
接口的继承
1: package main
2:
3: import "fmt"
4:
5: type Human interface { // 子集
6: sayHi()
7: }
8:
9: type Person interface { //超集
10: Human // 匿名字段
11: sing(lrc string)
12: }
13:
14: type Student struct{
15: name string
16: id int
17: }
18:
19: func (a *Student) sayHi() {
20: fmt.Printf("Student[%s, %d] sayhi\n", a.name, a.id)
21: }
22:
23: func (a *Student) sing(lrc string) {
24: fmt.Println("Student在唱着:", lrc)
25: }
26:
27: func main() {
28: //定义一个接口类型的变量
29: var i Person
30: s := &Student{"Mike", 666}
31: i = s
32: i.sayHi() //继承来的方法
33: i.sing("学生歌")
34: }
1: >>> Student[Mike, 666] sayhi
2: Student在唱着: 学生歌
接口的转换
如上,超集可以转换为子集,反过来不可以
1: func main() {
2: var iPro Person // 超集
3: iPro = &Student{"Mike", 666}
4: var i Human // 子集
5: // iPro = i 错误
6: i = iPro
7: i.sayHi()
8: }
1: >>> Student[Mike, 666] sayhi
空接口
空接口不包含任何的方法。因此空接口可以存储任意类型的数值,因此空接口是可以存储任意类型的数值。
1: var v1 interface{} = 1
2: var v2 interface{} = "abc"
3: var v3 interface{} = &v2
4: var v4 interface{} = struct{x int }{1}
5: var v5 interface{} = &struct{x int}{1}
当函数可以接受任意的对象实例时,我们会将其声明为interface{},最典型的例子是标准库fmt中PrintXXX系列的函数,例如:
1: func Printf(fmt string,args ...interface{})
2: func Println(args ...interface{})
类型查询
我们知道interface的变量里面可以存储任意类型的数值。反向知道这个变量里面实际保存了的是那个类型的对象,两种方法
comma-ok断言
1: type Student struct {
2: name string
3: id int
4: }
5:
6: func main() {
7: i := make([]interface{}, 3)
8: i[0] = 1
9: i[1] = "hello go"
10: i[2] = Student{"mike", 666}
11:
12: // 类型查询,类型断言
13: for index, data := range i {
14: if value, ok := data.(int); ok == true {
15: fmt.Printf("x[%d] 类型为int,内容为%d\n", index, value)
16: } else if value, ok := data.(string); ok == true {
17: fmt.Printf("x[%d] 类型为string,内容为%s\n", index, value)
18: } else if value, ok := data.(Student); ok == true {
19: fmt.Printf("x[%d] 类型为Student,内容为%s,%d\n", index, value.name, value.id)
20: }
21: }
22: }
switch测试
1: func main() {
2: i := make([]interface{}, 3)
3: i[0] = 1
4: i[1] = "hello go"
5: i[2] = Student{"mike", 666}
6:
7: for index,data := range i {
8: switch value := data.(type){
9: case int:
10: fmt.Printf("x[%d] 类型为int,内容为%d\n", index, value)
11: case string:
12: fmt.Printf("x[%d] 类型为string,内容为%s\n", index, value)
13: case struct:
14: fmt.Printf("x[%d] 类型为Student,内容为%s,%d\n", index, value.name, value.id)
15: }
16: }
17: switch data.(type)
18: }