接口interface

1.接口介绍

在Go语言中接口(interface)是一种类型,一种抽象的类型;
interface是一组method的集合,是duck-type programming的一种体现。接口做的事情就像是定义一个协议(规则),只要一台机器有洗衣服和甩干的功能,我就称它为洗衣机。不关心属性(数据),只关心行为(方法);
为了保护你的Go语言职业生涯,请牢记接口(interface)是一种类型;
image-20220106085750744

2.接口定义

2.1定义要求

image-20220106091804878

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 语句介绍

image-20220105140922986
image-20220105140939918

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
}