在Go语言中接口(interface)是一种类型,一种抽象的类型;
interface是一组method的集合,是duck-type programming的一种体现。接口做的事情就像是定义一个协议(规则),只要一台机器有洗衣服和甩干的功能,我就称它为洗衣机。不关心属性(数据),只关心行为(方法);
为了保护你的Go语言职业生涯,请牢记接口(interface)是一种类型;
type Phone interface {
demo//不会报错
}
type demo interface {
}
type Phone interface {
aa demo//会编译报错
}
type demo interface {
}
// 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()
}
接口只有方法声明,没有实现,没有数据字段
实现接口条件:
接口被实现的条件一:接口的方法与实现接口的类型方法格式一致
接口被实现的条件二:接口中所有方法均被实现
任何类型的方法集中只要拥有该接口’对应的全部方法’签名就表示它 “实现” 了该接口,无须在该类型上显式声明实现了哪个接口,这称为Structural Typing;
所谓对应方法,是指有相同名称、参数列表 (不包括参数名) 以及返回值;
当然该类型还可以有其他方法
// 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("喵喵喵")
}
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()
}
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() // 汪汪汪
}
// 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() //输出:旺财会动
}
// 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()//输出:旺财会叫汪汪汪
}
type Phone interface {
func() string //匿名时候需要加func
say() string //不匿名时候不用加func
}
接口类型的变量可使用其实例的方法 ,但是不可以使用其实例的字段
// 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() // 汪汪汪
}
// 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) //会编译出错
}
从下面的代码中我们可以发现,使用值接收者实现接口之后,不管是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()
}
此时实现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()
}
// 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迈
}
接口的方法可以通过在类型中嵌入其他类型或者结构体来实现
// 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() //输出:甩一甩
}
上面的代码中定义了猫和狗,然后它们都会叫,你会发现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())
}
package main
import (
"fmt"
)
type Phone interface {
call()
}
//会编译报错
func (i Phone) call() {
fmt.Println("q23erg")
}
func main() {
}
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
}
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("类型断言失败")
}
//输出:类型断言失败
}
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
}