接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节。
在Go语言中接口(interface)是一种类型,一种抽象的类型。
interface是一组method的集合,的duck-type programming的一种体现。接口做的事情就像是定义一个协议(规则),只要一台机器有洗衣服和甩干的功能,我就称它为洗衣机。不关系属性(数据),只关心行为(方法)。
type 接口类型名 interface{
方法名1(参数列表1) 返回值列表1
方法名2(参数列表2) 返回值列表2
...
}
其中:
- 接口名:使用type将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er,如有写操作的接口叫Writer,有字符串功能的接口叫Stringer等。接口名最好能突出该接口的类型含义。
- 方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
- 参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略。
例子:
type Writer interface{
Write([]byte) error
}
当你看到这个接口类型的值时,你不知道它是什么,唯一知道的就是可以通过它的Write方法来做一些事情。
一个对象只要全部实现了接口中的方法,那么就实现了这个接口。换句话说,接口就是一个需要实现的方法列表
例如:
// 先定义一个Sayer接口
type Sayer interface {
Say()
}
// 定义dog和cat两个结构体
type dog struct{}
type cat struct{}
// 因为Sayer接口里只有一个Say方法,所以我们只需要给dog和cat分别实现Say方法就可以实现Sayer接口
func (d dog) Say(){
fmt.Println("汪汪汪")
}
func (c cat) Say(){
fmt.Println("喵喵喵")
}
接口的实现就是这么简单,只要实现了接口中的所有方法,就实现了这个接口。
接口类型变量能够存储所有实现了该接口的实例。
例如上面的示例中,Sayer类型的变量能够存储dog和cat类型的变量。
func main() {
var sayer Sayer // 声明一个Sayer类型的变量sayer
cat := cat{} // 实例化一个cat
dob := dog{} // 实例化一个dog
sayer = a // 可以把cat实例直接赋值给sayer
sayer.say() // 喵喵喵
sayer = b // 可以把dog实例直接赋值给sayer
sayer.say() // 汪汪汪
}
// 定义一个接口Mover
type Mover interface{
move()
}
// 定义一个dog结构体
type dog struct{}
func (d dog) move{
fmt.Println("小狗在欢乐的奔跑")
}
func main() {
var x Mover // 声明一个Mover类型的变量x
var bmax = dog{} // bmax是dog类型
x = bmax // x可以接收dog类型
var qiuqiu = &dog{} // qiuqiu是*dog类型
x = qiuqiu // x可以接收*dog类型
x.move()
}
从上面的代码中我们可以发现,使用值接收者实现接口之后,不管是dog结构体还是结构体指针*dog类型的变量都可以赋值给该接口变量。因为Go语言中有对指针类型变量求值的语法糖,dog指针qiuqiu内部会自动求值*qiuqiu
func (d *dog) move() {
fmt.Println("小狗在欢乐的奔跑")
}
func main() {
var x Mover
var wangcai = dog{}
// x = wangcai // 报错,x不可以接收dog类型
var fugui = &dog{}
x = fugui
x.move()
}
此时实现Mover接口的是*dog类型,所以不能给x传入dog类型的wangcai,此时x只能存储*dog类型的值。
一个类型可以同时实现多个接口,而接口间彼此独立,不知道对方的实现。
例如,狗可以叫,也可以动。我们就分别定义Sayer接口和Mover接口:
// Sayer接口
type Sayer interface {
say()
}
// Mover接口
type Mover interface {
move()
}
dog即可以实现Sayer接口,也可以实现Mover接口。
type dog struct{
name string
}
// 实现Sayer接口
func (d dog) say() {
fmt.Printf("%s会叫汪汪汪\n", d.name)
}
// 实现Mover接口
func (d dog) move() {
fmt.Printf("%s会动\n", d.name)
}
func main() {
var x Sayer
var y Mover
var a = dog{name:"旺财"}
x = a
y = a
x.say()
y.move( )
}
Go语言中不同的类型还可以实现同一接口
type Mover interface{
move()
}
type dog struct{
name string
}
type car struct {
brand string
}
// dog类型实现Mover接口
func (d dog) move {
fmt.Printf("%s会跑\n", d.name)
}
// car类型实现Mover接口
func (c car) move {
fmt.Printf("%s速度70迈\n", c.brand)
}
func main() {
var x Mover
a := dog{name:"旺财"}
b := car{brand:"凯迪拉克"}
x = a
x.move()
x = b
x.move()
}
并且一个接口的方法,不一定需要由一个类型完全实现,接口的方法可以通过在类型中嵌入其他类型或者结构体来实现。
type WashingMachine interface {
wash()
dry()
}
type dryer struct {}
func (d dryer) dry() {
fmt.Println("甩一甩")
}
type haier struct{
dryer // 嵌入甩干器
}
func (h haier) wash() {
fmt.Println("洗刷刷")
}
func main() {
var w WashingMachine
d := dryer{}
h := haier{d}
w = h
w.dry()
w.wash()
}
接口与接口之间可以通过嵌套创造出新的接口。
type Sayer interface{
say()
}
type Mover interface{
move()
}
// 接口嵌套
type animal interface{
Sayer
Mover
}
嵌套得到的接口的使用与普通接口一样,这里我们让cat实现animal接口:
type cat struct{
name string
}
func (c cat) say() {
fmt.Println("喵喵喵")
}
func (c cat) move() {
fmt.Println("小猫上蹿下跳")
}
func main() {
var x animal = Cat{name: "花花"}
x.move()
x.say()
}
空接口是指没有定义任何方法的接口。因此任何类型都实现了空接口。
空接口类型的变量可以存储任意类型的变量。
func main() {
// 定义一个空接口x
var x interface{}
s := "Hello August"
x = s
fmt.Printf("type:%T value:%v\n",x, x)
i := 100
x = i
fmt.Printf("type:%T value:%v\n",x, x)
b := true
x = b
fmt.Printf("type:%T value:%v\n",x, x)
}
使用空接口实现可以接收任意类型的函数参数。
// 空接口作为函数参数
func show (a interface{}) {
fmt.Printf("type:%T value:%v\n",a, a)
}
// 不定长参数
func show2 (args ...interface{}) {
for _, arg := range args {
fmt.Println(arg)
}
}
使用空接口实现可以保存任意值的字典。
var studentInfo = make(map[string]interface{})
studentInfo["name"] = "August Rush"
studentInfo["age"] = 18
studentInfo["maried"] = false
fmt.Println(studentInfo)
一个接口的值(简称接口值)是由一个具体类型
和具体类型的值
两部分组成的。这两部分分别称为接口的动态类型
和动态值
想要判断空接口中的值 这个时候就可以使用类型断言,其语法格式:
x.(T)
其中:
interface{}
的变量x
可能是的类型该语法返回两个参数,第一个参数是x
转化为T
类型后的变量,第二个值是一个布尔值,若为true
则表示断言成功,为false
则表示断言失败。
func main() {
var x interface{}
x = "August Rush"
v, ok := x.(string)
if ok {
fmt.Println(v)
} else {
fmt.Println("不是string")
}
}
当需要断言多次就需要写多个if
判断,可以用switch
来体态:
func justifyType(x interface{}) {
switch v := x.(type) {
case string:
fmt.Printf("x is a string, value is %v\n", v)
case int:
fmt.Printf("x is a int, value is %v\n", v)
case bool:
fmt.Printf("x is a bool, value is %v\n", v)
default:
fmt.Println("unsupported type!")
}
}
关于接口需要注意的是,只有当有两个或两个以上的具体类型必须以相同的方式进行处理时才需要定义接口。不要为了接口而写接口,那样只会增加不必要的抽象,导致不必要的运行时损耗。
基于Nginx+Supervisord+uWSGI+Django1.11.1+Python3.6.5构建