首页 > 编程笔记 > Go语言笔记

Go语言接口的定义和使用

Go 语言提供了一种称为接口(interface)的数据类型,它代表一组方法的集合。接口的组合、嵌套和鸭子类型(Duck Typing)等实现了代码复用、解耦和模块化等特性,而且接口是方法动态分派、反射的基础功能。

接口设计是非侵入式的,接口设计者无须知道接口被哪些类型实现。而接口使用者只需知道实现怎样的接口,无须指明实现哪一个接口。编译器在编译时就会知道哪个类型实现哪个接口,或者接口应该由谁来实现。

非侵入式设计是 Go 语言设计者经过多年的大项目经验总结出来的设计之道,让接口和实现者真正解耦,编译速度才能真正提高,同时降低项目之间的耦合度。

接口是双方约定的一种合作协议,它是一种类型,也是一种抽象结构,不会暴露所含数据格式、类型及结构。

接口语法定义如下:
type interface_name interface {
   method1(parameter) [returnType]
   method2(parameter) [returnType]
   method3(parameter) [returnType]
   ...
   methodN(parameter) [returnType]
}
1)接口使用关键字 type 定义;关键字 type 后面设置接口名称 interface_name;接口名称后面设置接口类型 interface,interface 是 Go 语言的关键字。

2)接口里面的 method1、method2、…、methodN 代表方法名;parameter 代表参数名称和数据类型,如果方法没有参数,则无须设置;returnType 代表返回值的数据类型,如果方法没有返回值,则无须设置。

3)当接口方法的首个字母是大写格式,并且接口的首个字母也是大写格式时,将接口和方法设为导出标识符,它们可以被其他 go 文件访问。

根据接口语法定义,在同一接口定义不同类型的方法,示例如下:
type actions interface {
    // 没有参数,也没有返回值
    walk()
    // 没有参数,有返回值
    runs() (int, int)
    // 有参数,没有返回值
    speak(content string, speed int)
    // 有参数,也有返回值
    rest(sleepTime int) (int)
}

接口方法只需设置方法名称、参数名称和数据类型、返回值的数据类型,无须在接口中编写方法的业务功能,而方法的业务功能由结构体方法实现,示例如下:
package main

import "fmt"

// 定义接口
type actions interface {
    // 没有参数,也没有返回值
    walk()
    // 没有参数,有返回值
    runs() (int, int)
    // 有参数,没有返回值
    speak(content string, speed int)
    // 有参数,也有返回值
    rest(sleepTime int) int
}

// 定义结构体
type cats struct {
    name string
}

// 定义接口方法的功能逻辑
func (c *cats) walk() {
    fmt.Printf("%v在散步\n", c.name)
}

func (c *cats) runs() (int, int) {
    fmt.Printf("%v在跑步\n", c.name)
    speed := 10
    time := 1
    return speed, time
}

func (c *cats) speak(content string, speed int) {
    fmt.Printf("%v在说话:%v,语速:%v\n", c.name, content, speed)
}

func (c *cats) rest(sleepTime int) int {
    fmt.Printf("%v在休息,入睡时间:%v小时\n", c.name, sleepTime)
    return sleepTime
}

func main() {
    // 定义接口变量
    var a actions
    // 结构体实例化
    c := cats{name: "kitty"}
    // 结构体实例化变量的指针赋值给接口变量
    a = &c
    // 调用接口中的方法
    a.walk()
    speed, time := a.runs()
    fmt.Printf("跑步速度:%v,跑步时间:%v\n", speed, time)
    a.speak("喵喵", 2)
    sleepTime := a.rest(10)
    fmt.Printf("入睡时间:%v小时\n", sleepTime)
}
上述代码的实现过程说明如下:
1) 定义接口 actions 和结构体 cats,接口 actions 分别设置 4 个方法:walk()、runs()、speak() 和 rest();结构体 cats 只设置成员 name,其数据类型为字符串类型。

2) 结构体 cats 定义 4 个方法,每个方法分别对应接口 actions 的方法,也就是接口 actions 的方法通过结构体方法实现具体功能逻辑。将结构体绑定接口,结构体必须为接口中的每个方法定义相应的结构体方法,否则程序提示 as some methods are missing 异常。

3) 主函数 main() 定义接口变量 a,将结构体 cats 实例化生成实例化变量 c;然后把变量 c 的内存地址赋值给变量 a,结构体 cats 绑定在接口变量 a 中。

4) 如果结构体方法的接收者是值接收者,只要把实例化变量 c 赋值给变量 a 即可,接口变量 a 或结构体实例化变量 c 都可以调用接口方法。

运行上述代码,运行结果如下图所示。

kitty在散步
kitty在跑步
跑步速度:10,跑步时间:1
kitty在说话:喵喵,语速:2
kitty在休息,入睡时间:10小时
入睡时间:10小时

接口变量 a 只能调用接口 actions 中定义的方法,如果结构体 cats 还定义了接口 actions 之外的结构体方法,接口变量 a 是无法调用的,并且接口变量 a 也无法访问结构体成员,接口之外的结构体方法只能由结构体实例化变量调用。

综上所述,接口的定义与使用总结如下:

相关文章