【Go语言】接口编程
一、接口的基本概念
接口的定义
- 接口是一组行为规范的集合
- 接口名通常以 er 结尾
- 接口里只定义方法,不定义变量
- 参数列表和返回值列表里的变量名可以省略
type Transporter interface { distance(src string, dest string) float64 expenses(int) int takeTime(float64,float64) time.Time }
接口的实现
- 定义结构体时无需显式声明要实现的对应接口,只要结构体拥有接口里声明的所有方法,就称该结构体“实现了接口”
- 一个结构体可以实现多个接口
Car 实现接口 Transporter
type Transporter interface { distance(src string, dest string) float64 expenses(src string, dest string) int takeTime(src string, dest string) float64 } var city = map[string]int{ "bj": 31893, "sz": 34348, "hk": 34987, } type Car struct { model string speed float64 cost float64 } func (Car) distance(src, dest string) float64 { s := float64(city[src] - city[dest]) s = math.Abs(s) return s } func (car Car) expenses(src, dest string) int { c := car.distance(src, dest) * 9.2 * car.cost return int(c) } func (car Car) takeTime(src, dest string) float64 { t := car.distance(src, dest) / car.speed return t }
接口的本质
接口值由两部分组成:
指向该接口的具体类型的指针(结构体类型定义的指针)
指向该具体类型真实数据的指针(实例化的结构体变量的值)
接口的使用
范例:
type Transporter interface { distance(src string, dest string) float64 expenses(src string, dest string) int takeTime(src string, dest string) float64 } var city = map[string]int{ "bj": 31893, "sz": 34348, "hk": 34987, } type Car struct { model string speed float64 cost float64 } func (Car) distance(src, dest string) float64 { s := float64(city[src] - city[dest]) s = math.Abs(s) return s } func (car Car) expenses(src, dest string) int { c := car.distance(src, dest) * 9.2 * car.cost return int(c) } func (car Car) takeTime(src, dest string) float64 { t := car.distance(src, dest) / car.speed return t } func Transportinfo(src, dest string, t Transporter) { s := t.distance(src, dest) times := t.takeTime(src, dest) c := t.expenses(src, dest) fmt.Printf("distance is %2.f KM\n", s) fmt.Printf("takeTime is %2.f h\n", times) fmt.Printf("expenses is ¥%d\n", c) } func main() { car := Car{"BMW", 120, 0.08} Transportinfo("bj", "sz", car) }输出结果:
接口的赋值
实现指针的结构体方法中如果全部使用 值接收 方法,则在接口赋值时可以使用 值赋值 或 指针赋值
如果实现指针的结构体方法中存在 指针接收 方法,则在接口赋值时只能使用 指针赋值
实现了接口的结构体可以直接赋值给接口
type Transporter interface { distance(src string, dest string) float64 expenses(src string, dest string) int takeTime(src string, dest string) float64 } // 方法接收的是值 func (car Car) expenses(src, dest string) int { c := car.distance(src, dest) * 9.2 * car.cost return int(c) } // 方法接收指针,则实现接口的是指针类型 func (plane *Plane) expenses(src, dest string) int { c := (*plane).distance(src, dest)*3*float64((*plane).level) + float64((*plane).base) return int(c) } func main() { car := Car{"BMW", 120, 0.08} plane := Plane{1, 800, 10, 50} var t Transporter t = car // 方法都是接收值,接口赋值可以使用值赋值 println(t.expenses("bj", "sz")) t = &car // 方法都是接收值,接口赋值也可以实现指针赋值 println(t.expenses("bj", "sz")) t = &plane // 存在方法接收指针,接口赋值必须使用指针赋值 println(t.expenses("bj", "sz")) }
二、接口嵌入
type Transporter interface { distance(src string, dest string) float64 expenses(src string, dest string) int takeTime(src string, dest string) float64 } type Plan interface { Transporter IsAllow(int) bool }
三、空接口
- 空接口类型用 interface{} 表示
- 空接口没有定义任何方法,因此任意类型都实现了空接口
- func squrae(x interface{}) {} 该函数可以接收任意数据类型作为入参
- slice 元素、map 的 key,value 都可以是空接口类型
四、类型断言
在未知变量对应数据类型时,通过假设类型进行类型判断
当需要进行判断的类型较多时,需要使用很多次的 if-else,更好方案是使用 switch i.(type) 可参考:https://www.cnblogs.com/Janzen-q/p/17591775.html
func type_if (i interface{}){ if v,ok :=i.(int); ok { // 此时 i 为未知类型的变量,进行类型断言int,如果断言成功,ok 为 true, v 是具体类型的值 fmt.Printf("i type is int, i = %d",v) } else { fmt.Printf("i type is not int") } }
五、面向接口编程
- 在框架层面全是接口
- 具体的实现由不同的开发者去完成,每种实现单独放到一个go文件中,代码之间互不干扰
- 通过配置选择采用的实现方式,便于进行效果对比
案例项目链接:https://files.cnblogs.com/files/blogs/693302/rec.zip?t=1691431448&download=true