ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[TOC] # 接口 谨记:接口(interface)是一种类型,一种抽象的类型 ## 接口类型 接口是一种由程序员来定义的类型,一个接口类型就是一组方法的集合,它规定了需要实现的所有方法。 ### 接口的定义 每个接口类型由任意个方法签名组成,格式如下: ``` type interface_name interface{ function_name1( [param] ) [return_values] function_name2( [param] ) [return_values] … } ``` 其中: * type关键字表示要自定义类型; * interface_name是自定义的接口名;interface表示接口类型;由大括号包裹的部分定义了要被实现方法,一个接口中可以同时存在一个或多个方法。 * function_name是方法名;params是方法所需的参数,return_values是方法的返回值。params和return_values可以省略,也可以存在一个或多个。 * 在为接口命名时,一般会在单词后面加上er后缀 eg: ``` type personer interface{ … } ``` ## 接口的实现 接口就是规定了一个**需要实现的方法列表**,在 Go 语言中一个类型只要实现了接口中规定的所有方法,那么我们就称它实现了这个接口。 ``` type player interface{ play() } type jinglecat struct{} func (j *jinglecat) play(){ fmt.Println("播放了3集~") } ``` 因为`player`接口只包含一个`play`方法,所以只需要给`jinglecat`结构体添加一个`play`方法就可以满足`player`接口的要求,此时就可以说是实现了接口 ### 为什么要使用接口 ``` type jinglecat struct{} func (j *jinglecat) play(){ fmt.Println("小叮当播放了3集~") } type naruto struct{} func (n *naruto) play(){ fmt.Println("火影播放了3集~") } func main(){ cat := new(jinglecat) cat.play() //小叮当播放了3集~ nar := new(naruto) nar.play() //火影播放了3集~ } ``` 还有很多的动画片也播放3集了那就需要写很多的以上重复代码,那么,定义一个接口 ``` type player interface{ play() } func comicPlay(p player) {     p.play() } func main() { var cater player cater = new(jinglecat) comicPlay(cater) //小叮当播放了3集~ cater = &naruto{} comicPlay(cater)//火影播放了3集~ } ``` 就相当于面向接口编程,根本不关心某个类型具体的play怎么实现,我们关心play能不能正常实现 ## 值接收者和指针接收者 ### 值接收者实现接口 ``` type player interface{ play() } type jinglecat struct{} func (j jinglecat) play(){ fmt.Println("小叮当播放了3集~") } type naruto struct{} func (n *naruto) play(){ fmt.Println("火影播放了3集~") } func main(){ cat := jinglecat{} cat.play() //小叮当播放了3集~ } ``` ### 指针接收者实现接口 ``` type player interface{ play() } type naruto struct{} func (n *naruto) play(){ fmt.Println("火影播放了3集~") } func main(){ nar := new(naruto) //或者&naruto{} nar.play() //火影播放了3集~ } ``` 区别: * 在接口背景下,对于值接收者实现的接口,无论使用值类型还是指针类型都没有问题,但是当是指针类型时候只能是指针。 * 在非接口情况下,值类型和指针类型的方法,可以互相调用 ## 类型与接口的关系 ### 一个类型实现多个接口 ``` type mover interface { move() } type sayer interface { say() } type cat struct { Name string } func (c *cat) move() {     fmt.Printf("%s 咔咔咔跑 \n", c.Name) } func (c *cat) say() {     fmt.Printf("%s wawa叫 \n", c.Name) } func main() { var d1 mover x := &cat{ Name: "叫我小叮当啊",     } d1 = x     d1.move() //叫我小叮当啊 咔咔咔跑 var d2 sayer d2 = x     d2.say() //叫我小叮当啊 wawa叫 } ``` ### 多种类型实现同一接口 ``` type mover interface { move() } type cat struct { name string } func (c *cat) move() { fmt.Printf("%s 咔咔跑 \n", c.name) } type car struct { name string } func (c *car) move() { fmt.Printf("%s 呜呜跑~ \n", c.name) } func main() { var moveType mover dog := &cat{ name: "叫我小叮当啊", } moveType = dog moveType.move()//叫我小叮当啊 咔咔跑 car := &car{ name: "火车", } moveType = car moveType.move()//火车 呜呜跑~ } ``` ### 嵌入结构体实现接口 ``` type personer interface { eat() drink() } type man struct { sex string } func (m *man) eat() { fmt.Printf("%s 会吃肉 \n", m.sex) } type women struct { name string man } func (w *women) drink() { fmt.Printf("名字是%s 的 %s 还能喝酒\n", w.name, w.sex) } func main() { var person personer women := &women{ name: "露娜", man: man{ sex: "女人", }, } person = women person.eat() person.drink() } ``` ### 接口互相嵌套 ``` type personer interface { drinker eater } type drinker interface { drink() } type eater interface { eat() } type wan struct { name string } func (w *wan) eat() { fmt.Printf("%s 贼能吃\n", w.name) } func (w *wan) drink() { fmt.Printf("%s 还贼能喝\n", w.name) } func main() { var newperson personer wan := &wan{ name: "叫我小叮当啊", } newperson = wan newperson.eat() newperson.drink() } ``` ## 空接口 ### 空接口的定义 空接口是指没有定义任何方法的接口类型。因此任何类型都可以视为实现了空接口。也正是因为空接口类型的这个特性,空接口类型的变量可以存储任意类型的值。 ``` // Any 不包含任何方法的空接口类型 type Any interface{} func main() { var x Any x = "你好" // 字符串型 fmt.Printf("type:%T value:%v\n", x, x) x = 100 // int型 fmt.Printf("type:%T value:%v\n", x, x) x = true // 布尔型 fmt.Printf("type:%T value:%v\n", x, x) } ``` 通常我们在使用空接口类型时不必使用`type`关键字声明,可以像下面的代码一样直接使用`interface{}`。 ``` var x interface{} // 声明一个空接口类型变量x ``` ### 空接口的应用 #### 空接口作为函数的参数 使用空接口实现可以接收任意类型的函数参数。 ``` // 空接口作为函数参数 func show(a interface{}) { fmt.Printf("type:%T value:%v\n", a, a) } ``` #### 空接口作为map的值 使用空接口实现可以保存任意值的字典。 ``` var studentInfo = make(map[string]interface{}) studentInfo["name"] = "叫我小叮当啊" studentInfo["age"] = 18 studentInfo["gener"] = "男" fmt.Println(studentInfo) ``` ## 接口值 由于接口类型的值可以是任意一个实现了该接口的类型值,所以接口值除了需要记录具体**值**之外,还需要记录这个值属于的**类型**。也就是说接口值由“类型”和“值”组成,鉴于这两部分会根据存入值的不同而发生变化,我们称之为接口的`动态类型`和`动态值`。 ![](https://img.kancloud.cn/73/19/73190886381a4c5257c6a84596df3393_454x278.png) ``` type Mover interface { Move() } type Dog struct { Name string } func (d *Dog) Move() { fmt.Println("狗在跑~") } type Car struct { Brand string } func (c *Car) Move() { fmt.Println("汽车在跑~") } ``` 创建一个`Mover`接口类型的变量`m`。 ``` var m Mover ``` 此时,接口变量`m`是接口类型的零值,也就是它的类型和值部分都是`nil`,如图 ![](https://img.kancloud.cn/92/53/9253a41b2b2cd12e53cd817ee0b6f331_420x277.png) 可以使用`m == nil`来判断此时的接口值是否为空。 ``` fmt.Println(m == nil) // true ``` **注意:**我们不能对一个空接口值调用任何方法,否则会产生panic。 ``` m.Move() // panic: runtime error: invalid memory address or nil pointer dereference ``` 我们给接口变量`m`赋值为一个`*Car`类型的值。 ``` m = new(Car) ``` 接口值的动态类型为`*Car`,动态值为`nil` ![](https://img.kancloud.cn/53/5e/535e9cf5d4df54e0a5a381cb6f6b9bc9_845x304.png) **注意:**此时接口变量`m`与`nil`并不相等,因为它只是动态值的部分为`nil`,而动态类型部分保存着对应值的类型。 **接口值是支持相互比较的,当且仅当接口值的动态类型和动态值都相等时才相等。有两种数据是无法比较的,它们是:Map和Slice** ## 类型断言 格式: ``` value, ok := x.(T) ``` 其中: * x:表示接口类型的变量 * T:表示断言`x`可能是的类型。 ``` type Cube struct { length float64 } func (c *Cube) cubeVolume() float64 { return math.Pow(c.length, 3) } type Cuboid struct { length float64 width float64 height float64 } func (c *Cuboid) CuboidVolume() float64 { return c.height * c.length * c.width } type Cylinder struct { diameter float64 height float64 } func (c *Cylinder) CylinderVolume() float64 { return math.Pi * math.Pow((c.diameter/2), 2) * c.height } func calcSize(material interface{}) float64 { switch data := material.(type) { case Cube: return data.cubeVolume() case Cuboid: return data.CuboidVolume() case Cylinder: return data.CylinderVolume() default: return 0 } } func main() { countSize := 0.0 var sizeSlice []interface{} sizeSlice = append(sizeSlice, Cube{length: 12}) sizeSlice = append(sizeSlice, Cuboid{length: 12.0, width: 4.5, height: 3.0}) sizeSlice = append(sizeSlice, Cylinder{height: 3.0, diameter: 6.5}) for _, value := range sizeSlice { countSize += calcSize(value) } fmt.Println(countSize) } ```