1.接口介绍
在Go语言中接口(interface)是一种类型,一种抽象的类型;
interface是一组method的集合,是duck-type programming的一种体现。接口做的事情就像是定义一个协议(规则),只要一台机器有洗衣服和甩干的功能,我就称它为洗衣机。不关心属性(数据),只关心行为(方法);
为了保护你的Go语言职业生涯,请牢记接口(interface)是一种类型;
2.接口定义
2.1定义要求
2.2接口可以嵌入到其他接口里但必须得匿名
2.2.1接口里可以含一个接口时
type Phone interface {
demo//不会报错
}
type demo interface {
}
type Phone interface {
aa demo//会编译报错
}
type demo interface {
}
2.2.2接口里可以含多个接口时
// Sayer 接口
type Sayer interface {
say()
}
// Mover 接口
type Mover interface {
move()
}
// 接口嵌套
type animal interface {
Sayer
Mover
}
type cat struct {
name string
}
func (c cat) say() {
fmt.Println("喵喵喵")
}
func (c cat) move() {
fmt.Println("猫会动")
}
func main() {
var x animal
x = cat{name: "花花"}
x.move()
x.say()
}
2.3接口没有数据字段
接口只有方法声明,没有实现,没有数据字段
2.4接口可以嵌入到结构体struct里
2.5空接口可以作为任何类型数据的容器
2.6接口命名习惯以 er 结尾
2.7实现接口的所有方法即可实现接口
实现接口条件:
接口被实现的条件一:接口的方法与实现接口的类型方法格式一致
接口被实现的条件二:接口中所有方法均被实现
任何类型的方法集中只要拥有该接口’对应的全部方法’签名就表示它 “实现” 了该接口,无须在该类型上显式声明实现了哪个接口,这称为Structural Typing;
所谓对应方法,是指有相同名称、参数列表 (不包括参数名) 以及返回值;
当然该类型还可以有其他方法
2.7.1没有嵌套其他接口时
// Sayer 接口
type Sayer interface {
say()
}
type dog struct{}
type cat struct{}
// dog实现了Sayer接口
func (d dog) say() {
fmt.Println("汪汪汪")
}
// cat实现了Sayer接口
func (c cat) say() {
fmt.Println("喵喵喵")
}
2.7.1嵌套其他接口时正确实现
type Sayer interface {
Lister
say()
}
type Lister interface {
lis()
}
type cat struct{}
// dog实现了Sayer接口
func (d cat) say() {
fmt.Println("汪汪汪")
}
// cat实现了Sayer接口
func (c cat) lis() {
fmt.Println("喵喵喵")
}
func main() {
var x Sayer // 声明一个Sayer类型的变量x
a := cat{} // 实例化一个cat
x = a // 会报错,原因缺少say方法
x.say() // 汪汪汪
x.lis()
}
2.7.1嵌套其他接口时错误实现
type Sayer interface {
Lister
say()
}
type Lister interface {
lis()
}
type dog struct{}
type cat struct{}
// dog实现了Sayer接口
func (d dog) say() {
fmt.Println("汪汪汪")
}
// cat实现了Sayer接口
func (c cat) lis() {
fmt.Println("喵喵喵")
}
func main() {
var x Sayer // 声明一个Sayer类型的变量x
a := cat{} // 实例化一个cat
b := dog{} // 实例化一个dog
x = a // 会报错,原因缺少say方法
x = b // 会报错,原因缺少lis方法
x.say() // 汪汪汪
}
2.8接口是一个或多个方法签名的集合
2.9一个类型可以实现多个接口
2.9.1接口内方法不一样时
// Sayer 接口
type Sayer interface {
say()
}
// Mover 接口
type Mover interface {
move()
}
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() //输出:旺财会动
}
2.9.2接口内方法一样时
// Sayer 接口
type Sayer interface {
say()
}
// Mover 接口
type Mover interface {
say()
}
type dog struct {
name string
}
// 实现Sayer接口
func (d dog) say() {
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.say()//输出:旺财会叫汪汪汪
}
2.10接口同样支持匿名方法
type Phone interface {
func() string //匿名时候需要加func
say() string //不匿名时候不用加func
}
2.11只有当接口存储的类型和对象都为nil时接口才等于nil
2.12接口类型的变量可使用其实例的方法
接口类型的变量可使用其实例的方法 ,但是不可以使用其实例的字段
// Sayer 接口
type Sayer interface {
say()
}
type dog struct {
a int
}
type cat struct{}
// dog实现了Sayer接口
func (d dog) say() {
fmt.Println("汪汪汪")
}
// cat实现了Sayer接口
func (c cat) say() {
fmt.Println("喵喵喵")
}
func main() {
var x Sayer // 声明一个Sayer类型的变量x
a := cat{} // 实例化一个cat
b := dog{} // 实例化一个dog
x = a // 可以把cat实例直接赋值给x
x.say() // 喵喵喵
x = b // 可以把dog实例直接赋值给x
x.say() // 汪汪汪
}
2.13接口类型的变量不可以使用其实例的字段
// Sayer 接口
type Sayer interface {
say()
}
type dog struct {
a int
}
// dog实现了Sayer接口
func (d dog) say() {
fmt.Println("汪汪汪")
}
func main() {
var x Sayer
b := dog{
a :4,
}
x = b
x.say()
fmt.Println(x.a) //会编译出错
}
2.14值接收者实现接口
从下面的代码中我们可以发现,使用值接收者实现接口之后,不管是dog结构体还是结构体指针dog类型的变量都可以赋值给该接口变量。因为Go语言中有对指针类型变量求值的语法糖,dog指针fugui内部会自动求值fugui
type Mover interface {
move()
}
type dog struct{}
func (d dog) move() {
fmt.Println("狗会动")
}
func main() {
var x Mover
var wangcai = dog{} // 旺财是dog类型
x = wangcai // x可以接收dog类型
var fugui = &dog{} // 富贵是*dog类型
x = fugui // x可以接收*dog类型
x.move()
}
2.15指针接收者实现接口
此时实现Mover接口的是dog类型,所以不能给x传入dog类型的wangcai,此时x只能存储dog类型的值
type Mover interface {
move()
}
type dog struct{}
func (d *dog) move() {
fmt.Println("狗会动")
}
func main() {
var x Mover
var wangcai = dog{} // 旺财是dog类型
x = wangcai // 这一行会会议报错
var fugui = &dog{} // 富贵是*dog类型
x = fugui // x可以接收*dog类型
x.move()
}
2.16多个类型实同一接口
// Mover 接口
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
var a = dog{name: "旺财"}
var b = car{brand: "保时捷"}
x = a
x.move() //输出:旺财会跑
x = b
x.move() //输出:保时捷速度70迈
}
2.17结构体中方法+结构体中所嵌套的结构体的方法来实现接口
接口的方法可以通过在类型中嵌入其他类型或者结构体来实现
// WashingMachine 洗衣机
type WashingMachine interface {
wash()
dry()
}
// 甩干器
type dryer struct{}
// 实现WashingMachine接口的dry()方法
func (d dryer) dry() {
fmt.Println("甩一甩")
}
// 海尔洗衣机
type haier struct {
dryer //嵌入甩干器
}
// 实现WashingMachine接口的wash()方法
func (h haier) wash() {
fmt.Println("洗刷刷")
}
func main() {
var x WashingMachine
var a haier
x = a
x.dry() //输出:甩一甩
}
3.使用接口的原因
上面的代码中定义了猫和狗,然后它们都会叫,你会发现main函数中明显有重复的代码,如果我们后续再加上猪、青蛙等动物的话,我们的代码还会一直重复下去。那我们能不能把它们当成“能叫的动物”来处理呢?
像类似的例子在我们编程过程中会经常遇到:
比如一个网上商城可能使用支付宝、微信、银联等方式去在线支付,我们能不能把它们当成“支付方式”来处理呢?
比如三角形,四边形,圆形都能计算周长和面积,我们能不能把它们当成“图形”来处理呢?
比如销售、行政、程序员都能计算月薪,我们能不能把他们当成“员工”来处理呢?
Go语言中为了解决类似上面的问题,就设计了接口这个概念。接口区别于我们之前所有的具体类型,接口是一种抽象的类型。当你看到一个接口类型的值时,你不知道它是什么,唯一知道的是通过它的方法能做什么
type Cat struct{}
func (c Cat) Say() string { return "喵喵喵" }
type Dog struct{}
func (d Dog) Say() string { return "汪汪汪" }
func main() {
c := Cat{}
fmt.Println("猫:", c.Say())
d := Dog{}
fmt.Println("狗:", d.Say())
}
4.接口的方法错误定义
package main
import (
"fmt"
)
type Phone interface {
call()
}
//会编译报错
func (i Phone) call() {
fmt.Println("q23erg")
}
func main() {
}
5.空接口
5.1空结构体占用16字节
type demo interface {
}
func main() {
var a demo
fmt.Println(unsafe.Sizeof(a)) //输出16
var b interface{}
fmt.Println(unsafe.Sizeof(b)) //输出16
var c complex64
fmt.Println(unsafe.Sizeof(c)) //输出8
}
5.2类型断言
5.1定义
5.2v,ok=x.(Y)应用
func main() {
var x interface{}
x = "pprof.cn"
v, ok := x.(string)
if ok {
fmt.Println(v)
} else {
fmt.Println("类型断言失败")
}
//输出:pprof.cn
}
func main() {
var x interface{}
x = "pprof.cn"
v, ok := x.(bool)
if ok {
fmt.Println(v)
} else {
fmt.Println("类型断言失败")
}
//输出:类型断言失败
}
5.3type switch 语句介绍
5.4结合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 is %v\n", v)
case bool:
fmt.Printf("x is a bool is %v\n", v)
default:
fmt.Println("unsupport type!")
}
}
func main() {
var x interface{}
x = "pprof.cn"
justifyType(x) //输出:x is a string,value is pprof.cn
}