Go语言接口的声明和实现(附带实例)
Go 语言不是面向对象的编程语言(这是因为在 Go语言中没有类和继承),但是它提供了接口。
接口把所有具有共性的方法定义在一起,任何其他类型只要实现这些方法就是实现这个接口。
接口可以将不同类型绑定在一组公共方法上,实现多态和灵活的设计。
不同于面向对象的编程语言,Go 语言提供的接口是隐式接口。那么,应该如何理解“隐式接口”呢?在 Go 语言程序开发中,无须标明具体实现哪些接口,只要实现某个接口中的所有函数就说明这个接口被实现了。
在 Go 语言中,使用 type 和 interface 关键字声明接口,接口包含多个函数。声明接口的语法格式如下:
例如,在 io 包提供的 Writer 接口中,包含一个参数为字节数组的 Write() 函数;Write() 函数有两个返回值,一个是写入的字符数,另一个是在写入字符的过程中可能发生的错误。代码如下:
上述代码还可以省略参数列表和返回值列表中的参数变量名,代码如下:
Go 语言没有提供显式实现接口的工具,如 implements 关键字。在实现接口的过程中,要遵循以下两个规则:
下例演示如何实现已经声明的接口。
【实例】打印一辆小汽车的行驶过程。为行驶中的汽车声明接口 carInMotion,并定义用于实现这个接口的结构体 car。在接口中包含 4 个函数,它们分别是行驶函数(参数是汽车此刻的行驶速度)、刹车函数(返回值分别是刹车前的速度和刹车后的速度)、泊车函数和消耗燃油函数(参数分别为剩余燃油量和燃油平均消耗,返回值是汽车还能行驶的距离)。在结构体中,包含一个表示颜色的 string 类型字段 color。在 main() 函数中,依次实现上述 4 个接口,打印这辆小汽车的行驶过程。代码如下:
对实例程序进行归纳总结后,即可得到如下结论:
接口把所有具有共性的方法定义在一起,任何其他类型只要实现这些方法就是实现这个接口。
接口可以将不同类型绑定在一组公共方法上,实现多态和灵活的设计。
不同于面向对象的编程语言,Go 语言提供的接口是隐式接口。那么,应该如何理解“隐式接口”呢?在 Go 语言程序开发中,无须标明具体实现哪些接口,只要实现某个接口中的所有函数就说明这个接口被实现了。
在 Go 语言中,使用 type 和 interface 关键字声明接口,接口包含多个函数。声明接口的语法格式如下:
type 接口名称 interface{ 函数名 1(参数列表 1) 返回值列表 1 函数名 2(参数列表 2) 返回值列表 2 ... }参数说明如下:
- 接口名称:Go 语言内置接口的名称通常以 er 结尾;例如,Writer 是执行写操作的接口,Stringer 是实现字符串功能的接口,Closer 是实现关闭功能的接口;
- 函数名:当函数名和接口名的首字母都大写时,这个函数可以被接口所在包之外的 go 文件访问;
- 参数列表、返回值列表:可以省略参数列表和返回值列表中的参数变量名。
例如,在 io 包提供的 Writer 接口中,包含一个参数为字节数组的 Write() 函数;Write() 函数有两个返回值,一个是写入的字符数,另一个是在写入字符的过程中可能发生的错误。代码如下:
type Writer interface { Write(p []byte) (n int, err error) }
上述代码还可以省略参数列表和返回值列表中的参数变量名,代码如下:
type Writer interface { Write([]byte) (int, error) }
Go语言接口的实现
在 Go语言程序开发中,要使用声明的接口,首先要实现这个接口,即实现接口中的所有函数。Go 语言没有提供显式实现接口的工具,如 implements 关键字。在实现接口的过程中,要遵循以下两个规则:
- 接口中的函数与实现接口的函数在格式(即函数名称、参数列表和返回值列表)上保持一致;
- 接口中的所有函数都要被实现。
下例演示如何实现已经声明的接口。
【实例】打印一辆小汽车的行驶过程。为行驶中的汽车声明接口 carInMotion,并定义用于实现这个接口的结构体 car。在接口中包含 4 个函数,它们分别是行驶函数(参数是汽车此刻的行驶速度)、刹车函数(返回值分别是刹车前的速度和刹车后的速度)、泊车函数和消耗燃油函数(参数分别为剩余燃油量和燃油平均消耗,返回值是汽车还能行驶的距离)。在结构体中,包含一个表示颜色的 string 类型字段 color。在 main() 函数中,依次实现上述 4 个接口,打印这辆小汽车的行驶过程。代码如下:
package main import "fmt" //声明 main 包 //导入 fmt 包,用于打印字符串 type carInMotion interface { move(speed int) //为行驶中的汽车声明接口 brake() (int, int) //刹车函数。返回值分别是刹车前的速度和刹车后的速度 park() //泊车函数 //消耗燃油函数。参数分别为剩余燃油量和燃油平均消耗,返回值是汽车还能行驶的距离 consumeOil(fuelLeft float64, aver_consumption float64) (distance float64) } type car struct { color string //表示汽车的结构体 //车身颜色 } func (c *car) move(speed int) { //实现接口中的 move()函数 fmt.Printf("一辆%v 的汽车在以%vkm/h 的速度匀速行驶\n", c.color, speed) } func (c *car) brake() (int, int) { //实现接口中的 brake()函数 fmt.Printf("这辆%v 的汽车开始刹车\n", c.color) //刹车前的速度 speedBeforeBrake := 60 speedAfterBrake := 0 //刹车后的速度 return speedBeforeBrake, speedAfterBrake } func (c *car) park() { //实现接口中的 park()函数 fmt.Printf("这辆%v 的汽车停在路边的车位里\n", c.color) } //实现接口中的 consumeOil()函数 func (c *car) consumeOil(fuelLeft float64, aver_consumption float64) (distance float64) { fmt.Printf("这辆%v 的汽车剩余燃油%vL,燃油平均消耗%vL/100km\n", c.color, fuelLeft, aver_consumption) return fuelLeft / aver_consumption * 100 } func main() { var cim carInMotion c := car{color: "红色"} //声明接口变量 cim = &c //键值对初始化结构体变量执行“取地址”操作 cim.move(60) //调用 move()函数,并传递参数 speedBeforeBrake, speedAfterBrake := cim.brake() //调用 brake()函数,获取返回值 fmt.Printf("刹车前的速度是%vkm/h,刹车后的速度是%vkm/h\n", speedBeforeBrake, speedAfterBrake) cim.park() //调用 park()函数 distance := cim.consumeOil(27, 6.3) //调用 consumeOil()函数,先传递参数,再获取返回值 fmt.Printf("还能继续行驶%.2fkm\n", distance) //对计算结果保留两位小数 }运行结果如下:
一辆红色的汽车在以 60km/h 的速度匀速行驶
这辆红色的汽车开始刹车
刹车前的速度是 60km/h,刹车后的速度是 0km/h
这辆红色的汽车停在路边的车位里
这辆红色的汽车剩余燃油 27L,燃油平均消耗 6.3L/100km
还能继续行驶 428.57km
对实例程序进行归纳总结后,即可得到如下结论:
- 接口无法单独使用,须与结构体搭配使用;
- 使用接口前,须声明接口变量和实例化结构体。