12-Go语言之接口(interface)
内容目录
- 接口
内容目录
接口定义
- GO语言是面向接口编程
- 接口是一种抽象的类型
- 接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节。
每个接口由数个方法组成,接口的定义格式如下:
type 接口类型名 interface{
方法名1( 参数列表1 ) 返回值列表1
方法名2( 参数列表2 ) 返回值列表2
…
}
其中:
- 接口名:使用
type
将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er
,如有写操作的接口叫Writer
,有字符串功能的接口叫Stringer
等。接口名最好要能突出该接口的类型含义。 - 方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
- 参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略。
实现接口的条件
- 一个对象只要全部实现了接口中的方法,那么就实现了这个接口。换句话说,接口就是一个需要实现的方法列表。我们来定义一个
Sayer
接口: - 注意:接口内只有方法
// Sayer 接口
type Sayer interface {
say()
}
定义dog
和cat
两个结构体:
type dog struct {}
type cat struct {}
因为Sayer
接口里只有一个say
方法,所以我们只需要给dog
和cat
分别实现say
方法就可以实现Sayer
接口了。
// dog实现了Sayer接口
func (d dog) say() {
fmt.Println("汪汪汪")
}
// cat实现了Sayer接口
func (c cat) say() {
fmt.Println("喵喵喵")
}
接口的实现就是这么简单,只要实现了接口中的所有方法,就实现了这个接口。
接口使用
多态特性
-
使用接口可以实现面向对象中的多态,任何实现了接口中的方法,都可以用接口调用其本身的方法
// 接口实现一个人的特性 type Humaner interface { Say() } // 定义学生结构体 type Student struct { name string score int } // 定义学生方法 func (s *Student) Say() { fmt.Printf("Student[%s,%d]瞌睡不断\n", s.name, s.score) } // 定义老师结构体 type Teacher struct { name string group string } // 定义老师方法 func (t *Teacher) Say() { fmt.Printf("Teacher[%s,%s]毁人不倦\n", t.name, t.group) } // 自定义类型 type MyStr string // 自定义类型方法 func (str MyStr) Say() { fmt.Printf("MyStr[%s]原类型是string\n", str) } // 定义接口方法 func WhoSay(i Humaner){ i.Say() } func main() { s := &Student{"阿杰",88} // 实例化学生 t := &Teacher{"李老师","GO语言二期"}// 实例化老师 var tmp MyStr = "毒鸡汤" // 实例化自定义类型 // 方式一:接口调用,多态: WhoSay(s) WhoSay(t) WhoSay(tmp) // 方式二:接口调用,利用循环去调用方法 x := make([]Humaner,3) x[0] ,x[1],x[2] = s,t,tmp for _,value := range x{ value.Say() } } /* 方式一结果: Student[阿杰,88]瞌睡不断 Teacher[李老师,GO语言二期]毁人不倦 MyStr[毒鸡汤]原类型是string 方式二结果: Student[阿杰,88]瞌睡不断 Teacher[李老师,GO语言二期]毁人不倦 MyStr[毒鸡汤]原类型是string */
继承
-
使用一个接口继承另一个接口,也会继承接口的方法:
// 接口实现一个人的特性 type Humaner interface { Say() } // 用另一个接口继承Humaner接口 type Personer interface { Humaner Sing(lyrics string) } // 定义学生结构体 type Student struct { name string score int } // 定义学生方法 func (s *Student) Say() { fmt.Printf("Student[%s,%d]瞌睡不断\n", s.name, s.score) } // 定义学生方法 func (s *Student) Sing(lyrics string) { fmt.Printf("Student sing[%s]\n", lyrics) } func main() { s := &Student{"阿杰",88} // 实例化学生 var p Personer // 定义接口类型的变量 p = s // 赋值操作 p.Say() // 调用继承的方法 p.Sing("葫芦娃") // 调用继承的方法 } /* Student[阿杰,88]瞌睡不断 Student sing[葫芦娃] */
接口接收变量
-
接口可以接收任何实现了该接口内方法的变量。
-
接口类型变量:可以存储任何实现了该接口所有方法的对象类型。
// 接口实现一个动物的特性 type Animal interface { Talk() Eat() Name() string // 方法的返回值,直接写 } // 定义结构体 type Dog struct { } 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 // a相当于是一个变量 fmt.Println(a) // 此时a为空值,nil var d Dog // 接口类型可以存放Dog结构体,因为Dog实现了接口所有方法 a = d // 类绑定变量 a.Talk() a.Eat() a.Name() } /*
旺旺旺 啃骨头 名字,旺财 */
同一类型实现多个接口
-
同一个类型可以实现多个接口
// 接口实现一个动物的特性 type Animal interface { Talk() Eat() } // 第二个接口 type Animal2 interface { Run() } // 定义结构体 type Dog struct { } // 定义方法 func (d *Dog) Run() { fmt.Println("奔跑吧旺财") } func (d *Dog) Talk() { fmt.Println("旺旺旺") } func (d *Dog) Eat() { fmt.Println("啃骨头") } func main() { var a Animal var d Dog a = &d a.Eat() var a2 Animal2 a2 = &d a.Talk() // 同一个结构体可以实现多个接口 a2.Run() // 实现第二个接口 } /* 啃骨头 奔跑吧旺财 */
接口嵌套
-
接口嵌套后,必须实现嵌套接口内的所有方法才可使用
// 接口实现一个动物的特性 type Animal interface { Talk() Eat() } // 第二个接口 type Animal2 interface { Run() } // 第三个接口 type Animal3 interface { Animal // 嵌套第一个接口 Animal2 // 嵌套第二个接口 }
空接口
-
空接口是指没有定义任何方法的接口,因此任何类型都实现了空接口。
-
空接口类型的变量可以存储任意类型的变量。
func main(){ var x interface{} // 初始化一个空接口 x = 100 // 赋值操作 fmt.Println(x) // 打印的是int类型的100 x = "沙河" fmt.Println(x) // 输出为string类型 x = false fmt.Println(x) // 输出bool类型 x = struct { name string }{name:"花花"} fmt.Println(x) // 输出结构体类型 }
空接口的应用
空接口作为函数的参数
-
使用空接口实现可以接收任意类型的函数参数
// 空接口作为函数参数 func showType(a interface{}){ fmt.Printf("type:%T\n",a) } func main(){ showType(100) showType(99.99) showType(time.Second) } // type:int // type:float64 // type:time.Duration
空接口作为map的值
-
使用空接口实现可以保存任意值的字典。
func main(){ // 定义一个值为空接口的map var stuInfo = make(map[string]interface{},100) stuInfo["阿杰"] = 100 stuInfo["阿豪"] = true stuInfo["阿呆"] = 99.99 stuInfo["阿水"] = "阿珍爱上了阿强" stuInfo["时间"] = time.Now() fmt.Println(stuInfo) } /* map[ 时间:2020-10-19 17:45:51.4885587 +0800 CST m=+0.004011601 阿呆:99.99 阿杰:100 阿水:阿珍爱上了阿强 阿豪:true] */
值接收者和指针接收者实现接口的区别
-
值类型实现接口,指针类型可以存进去
-
但指针类型实现接口,值类型存不进去
- 示例一:值类型实现接口,指针类型可以存进去
// 接口实现一个动物的特性 type Animal interface { Talk() Eat() Name() string // 方法的返回值,直接写 } // 定义结构体 type Dog struct { } 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 // a相当于是一个变量 var d *Dog = &Dog{} // 定义Dog为指针类型 a = d // 类绑定变量 // 调用时,内部自动转*(&Dag).Eat() a.Talk() a.Eat() a.Name() }
- 示例二:指针类型实现接口,值类型存不进去
// 接口实现一个动物的特性 type Animal interface { Talk() Eat() Name() string // 方法的返回值,直接写 } // 定义结构体 type Dog struct { } 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 // a相当于是一个变量 var d Dog // 以下代码编译不通过原因: // 若传值类型,不能获取变量地址,d取不到地址,寻址问题不通过 a = d // 类绑定变量 a.Talk() a.Eat() a.Name() }
类型断言
-
想要判断空接口中的值这个时候就可以使用类型断言,语法格式为:
-
该语法返回两个参数,第一个参数是
x
转化为T
类型后的变量,第二个值是一个布尔值,若为true
则表示断言成功,为false
则表示断言失败。x.(T)
其中:
- x:表示类型为
interface{}
的变量 - T:表示断言
x
可能是的类型。
- x:表示类型为
-
例一:
// 示例一: func main() { var x interface{} x = "Hello 北京" v, ok := x.(string) // 进行类型断言 if ok { fmt.Println(v) } else { fmt.Println("类型断言失败") } } // 示例二:comma-ok断言 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{"小强", 18} for index, element := range list { // 类型断言:value,ok := 元素.(Type) if value, ok := element.(int); ok { fmt.Printf("list[%d]是int类型,值是%d\n", index, value) } else if value, ok := element.(string); ok { fmt.Printf("list[%d]是string类型,值是%v\n", index, value) } else if value, ok := element.(Person); ok { fmt.Printf("list[%d]是Person类型,值是[%s,%d]\n", index, value.name, value.age) } else { fmt.Println("不支持的类型") } } }
-
例二:可以用switch语句实现多种类型断言
func justifyType(x interface{}) { switch v := x.(type) { case string: fmt.Printf("x is a string,value is %v\n", v) case int: fmt.Printf("x is a int,value is %v\n", v) case bool: fmt.Printf("x is a bool,value is %v\n", v) case *string: fmt.Printf("x is a string poninter,value is %v\n", v) default: fmt.Println("unsupport type!") } } func main(){ justifyType(100) s := "你好" justifyType(&s) } /* x is a int,value is 100 x is a string poninter,value is 0xc00002e1f0 */