ArcherWong博客
首页
博客
golang笔记---接口(interface)
作者:ArcherWong
分类:golang
时间:2019-01-04 10:34:24
阅读:357
[TOC] # 1.接口介绍 ## 接口的定义 golang中接口定义了一组方法(方法集),但是这些方法不包含(实现)代码:它们没有被实现(它们是抽象的)。接口里也不能包含变量。 定义接口: ``` type Namer interface { Method1(param_list) return_type Method2(param_list) return_type ... } ``` - 类型不需要显式声明它实现了某个接口:接口被隐式地实现。多个类型可以实现同一个接口。 - 实现某个接口的类型(除了实现接口方法外)可以有其他的方法。 - 一个类型可以实现多个接口。 - 接口类型可以包含一个实例的引用, 该实例的类型实现了此接口(接口是动态类型) 举例 ``` package main import "fmt" type Shaper interface { Area() float32 } type Square struct { side float32 } func (sq *Square) Area() float32 { return sq.side * sq.side } func main() { sq1 := new(Square) sq1.side = 5 var areaIntf Shaper areaIntf = sq1 fmt.Printf("The square has area: %f\n", areaIntf.Area()) } ``` 输出: ``` The square has area: 25.000000 ``` 上面的程序定义了一个结构体 `Square` 和一个接口 `Shaper`,接口有一个方法 `Area()`。 在 `main()` 方法中创建了一个 Square 的实例。在主程序外边定义了一个接收者类型是 `Square` 方法的 `Area()`,用来计算正方形的面积:结构体 `Square` 实现了接口 `Shaper` 。 所以可以将一个 `Square` 类型的变量赋值给一个接口类型的变量:`areaIntf = sq1` 。 现在接口变量包含一个指向 `Square` 变量的引用,通过它可以调用 `Square` 上的方法 `Area()`。当然也可以直接在 `Square` 的实例上调用此方法,但是在接口实例上调用此方法更令人兴奋,它使此方法更具有一般性。接口变量里包含了接收者实例的值和指向对应方法表的指针。 如果 `Square` 没有实现 `Area()` 方法,编译器将会给出清晰的错误信息;如果 Shaper 有另外一个方法 Perimeter(),但是Square 没有实现它,即使没有人在 Square 实例上调用这个方法,编译器也会给出上面同样的错误。 ## 展示多态 示例 interfaces_poly.go: ``` package main import "fmt" type Shaper interface { Area() float32 } type Square struct { side float32 } func (sq *Square) Area() float32 { return sq.side * sq.side } type Rectangle struct { length, width float32 } func (r Rectangle) Area() float32 { return r.length * r.width } func main() { r := Rectangle{5, 3} // Area() of Rectangle needs a value q := &Square{5} // Area() of Square needs a pointer // shapes := []Shaper{Shaper(r), Shaper(q)} // or shorter shapes := []Shaper{r, q} fmt.Println("Looping through shapes for area ...") for n, _ := range shapes { fmt.Println("Shape details: ", shapes[n]) fmt.Println("Area of this shape is: ", shapes[n].Area()) } } ``` 输出: ``` Looping through shapes for area ... Shape details: {5 3} Area of this shape is: 15 Shape details: &{5} Area of this shape is: 25 ``` 在调用 shapes[n].Area() 这个时,只知道 shapes[n] 是一个 Shaper 对象,最后它摇身一变成为了一个 Square 或 Rectangle 对象,并且表现出了相对应的行为。 # 2.接口嵌套 一个接口可以包含一个或多个其他的接口,这相当于直接将这些内嵌接口的方法列举在外层接口中一样。 ``` type ReadWrite interface { Read(b Buffer) bool Write(b Buffer) bool } type Lock interface { Lock() Unlock() } type File interface { ReadWrite Lock Close() } ``` # 3.类型断言 一个接口类型的变量 varI 中可以包含任何类型的值,必须有一种方式来检测它的 动态 类型,通常我们可以使用 类型断言 来测试在某个时刻 varI 是否包含类型 T 的值: ``` v := varI.(T) // unchecked type assertion ``` **varI 必须是一个接口变量**,否则编译器会报错:invalid type assertion: varI.(T) (non-interface type (type of varI) on left) 。 类型断言可能是无效的,虽然编译器会尽力检查转换是否有效,但是它不可能预见所有的可能性。如果转换在程序运行时失败会导致错误发生。更安全的方式是使用以下形式来进行类型断言: ``` if v, ok := varI.(T); ok { // checked type assertion Process(v) return } // varI is not of type T ``` 如果转换合法,v 是 varI 转换到类型 T 的值,ok 会是 true;否则 v 是类型 T 的零值,ok 是 false,也没有运行时错误发生。 应该总是使用上面的方式来进行类型断言。 多数情况下,我们可能只是想在 if 中测试一下 ok 的值,此时使用以下的方法会是最方便的: ``` if _, ok := varI.(T); ok { // ... } ``` # 4. 方法集和接口的应用 **作用于变量上的方法实际上是不区分变量到底是指针还是值的。当碰到接口类型值时,这会变得有点复杂** ``` package main import ( "fmt" ) //定义接口 appender type Appender interface { Append(int) } // 定义接口 lener type Lener interface { Len() int } //定义一个结构体和方法集 type List []int func (l List) Len() int { return len(l) } func (l *List) Append(val int) { *l = append(*l, val) } //函数 func CountInto(a Appender, start, end int) { for i := start; i <= end; i++ { a.Append(i) } } //函数 func LongEnough(l Lener) bool { return l.Len()*10 > 42 } func main() { // 值 var lst List // CountInto(lst, 1, 10) 会报错,conutinto需要一个appender,是个指针 if LongEnough(lst) { fmt.Printf("- lst is long enough\n") } // 指针 plst := new(List) CountInto(plst, 1, 10) if LongEnough(plst) { fmt.Printf("- plst is long enough\n") } } ``` - 在 lst 上调用 CountInto 时会导致一个编译器错误,因为 CountInto 需要一个 Appender,而它的方法 Append 只定义在指针上。 在 lst 上调用 LongEnough 是可以的,因为 Len 定义在值上。 - 在 plst 上调用 CountInto 是可以的,因为 CountInto 需要一个 Appender,并且它的方法 Append 定义在指针上。 在 plst 上调用 LongEnough 也是可以的,因为指针会被自动解引用。 **参考资料** https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/directory.md
标签:
上一篇:
golang笔记--基本结构和基本数据类型
下一篇:
golang笔记---结构(struct)和方法(method)
文章分类
css
elasticsearch
git
golang
guacamole
javascript
letsencrypt
linux
nginx
other
php
python
vue
web
阅读排行
letsencrypt证书-管理工具certbot
centos7.3配置guacamole
golang笔记---关联数组(map)
golang笔记---template模板语法
nginx笔记-proxy_cache缓存详解
友情链接
node文件
laravel-vue
ArcherWong的博客园