目录
一、接口的介绍
二、Golang接口的定义
三、空接口
四、类型断言
五、结构体值接收者和指针接收者实现接口的区别
六、一个结构体实现多个接口
七、接口嵌套
一、接口的介绍
1. 现实生活中的接口
现实生活中,手机、相机、U盘都可以和电脑的USB接口建立连接。我们不需要关注USB卡槽大小是否一样,因为所有的USB接口都是按照统一的标准来设计的。
2. Golang中的接口(interface)
Golang中的接口是一种抽象数据类型 ,它定义了对象的行为规范,只定义规范,不实现。接口中定义的规范由具体的对象来实现。
通俗地说,接口就是一个标准,它约定实现接口的对象必须遵循接口的规范。
二、Golang接口的定义
在Golang中,接口(interface)是一种类型,一种抽象的类型 。接口是一组方法(method)的集合,接口中不能包含任何变量。
- 接口中的所有方法都没有方法体
- 接口定义了对象的行为规范,只定义规范不实现
- 接口体现了多态和高内聚低耦合的思想
- 接口是一种数据类型,不需要显式实现。只要一个变量含有接口类型中的所有方法,该变量就实现了这个接口
接口的定义格式
go
type 接口名 interface {
方法名1(参数列表1) 返回值列表1
方法名2(参数列表2) 返回值列表2
...
}
命名规范:
- 接口名通常使用
type定义为自定义类型名,一般会在单词后加er,如Writer、Stringer等 - 方法名首字母大写时,可被包外的代码访问
- 参数列表和返回值列表中的参数变量名可以省略
示例1:定义接口并由结构体实现
go
package main
import "fmt"
// 定义 Usber 接口
type Usber interface {
Start()
Stop()
}
// Phone 结构体实现 Usber 接口
type Phone struct {
Name string
}
func (p Phone) Start() {
fmt.Println(p.Name, "开始工作")
}
func (p Phone) Stop() {
fmt.Println(p.Name, "停止工作")
}
// Camera 结构体实现 Usber 接口
type Camera struct {
Name string
}
func (c Camera) Start() {
fmt.Println(c.Name, "开始工作")
}
func (c Camera) Stop() {
fmt.Println(c.Name, "停止工作")
}
func main() {
// Phone 实现接口
phone := Phone{Name: "小米手机"}
var p Usber = phone
p.Start()
p.Stop()
// Camera 实现接口
camera := Camera{Name: "佳能相机"}
var c Usber = camera
c.Start()
c.Stop()
}
示例2:接口作为函数参数
go
package main
import "fmt"
type Usber interface {
Start()
Stop()
}
type Phone struct {
Name string
}
func (p Phone) Start() {
fmt.Println(p.Name, "开始工作")
}
func (p Phone) Stop() {
fmt.Println(p.Name, "停止工作")
}
type Camera struct {
Name string
}
func (c Camera) Start() {
fmt.Println(c.Name, "开始工作")
}
func (c Camera) Stop() {
fmt.Println(c.Name, "停止工作")
}
// Computer 结构体的 Work 方法接收 Usber 接口类型
type Computer struct{}
func (c Computer) Work(usb Usber) {
usb.Start()
usb.Stop()
}
func main() {
computer := Computer{}
phone := Phone{Name: "小米手机"}
camera := Camera{Name: "佳能相机"}
computer.Work(phone)
computer.Work(camera)
}
三、空接口
Golang中的接口可以不定义任何方法,没有定义任何方法的接口就是空接口。
- 空接口表示没有任何约束
- 任何类型变量都可以实现空接口
- 空接口在实际项目中应用广泛,可表示任意数据类型
示例1:空接口接收任意类型
go
func main() {
var x interface{} // 定义空接口
s := "你好golang"
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)
}
示例2:空接口作为函数参数
go
func show(a interface{}) {
fmt.Printf("type:%T value:%v\n", a, a)
}
示例3:map 的值使用空接口
go
var studentInfo = make(map[string]interface{})
studentInfo["name"] = "张三"
studentInfo["age"] = 18
studentInfo["married"] = false
fmt.Println(studentInfo)
示例4:切片使用空接口
go
var slice = []interface{}{"张三", 20, true, 32.2}
fmt.Println(slice)
四、类型断言
接口值由具体类型 和具体类型的值 两部分组成,分别称为接口的动态类型 和动态值。
类型断言用于判断空接口中值的类型。
语法格式
go
x.(T)
x:类型为interface{}的变量T:断言x可能的类型
返回值
- 第一个值:
x转换为T类型后的变量 - 第二个值:布尔值,
true表示断言成功,false表示断言失败
示例1:单次类型断言
go
func main() {
var x interface{}
x = "Hello golang"
v, ok := x.(string)
if ok {
fmt.Println(v)
} else {
fmt.Println("类型断言失败")
}
}
示例2:使用 switch 进行类型断言
注意:
类型.(type)只能结合switch语句使用。
go
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 an int, value is %v\n", v)
case bool:
fmt.Printf("x is a bool, value is %v\n", v)
default:
fmt.Println("unsupported type!")
}
}
⚠️ 建议:只有当两个或两个以上的具体类型必须以相同方式处理时,才需要定义接口。不要为了接口而写接口,那样只会增加不必要的抽象和运行时损耗。
五、结构体值接收者和指针接收者实现接口的区别
值接收者
如果结构体中的方法是值接收者 ,那么结构体值类型 和结构体指针类型都可以赋值给接口变量。
go
package main
import "fmt"
type Usb interface {
Start()
Stop()
}
type Phone struct {
Name string
}
// 值接收者
func (p Phone) Start() {
fmt.Println(p.Name, "开始工作")
}
func (p Phone) Stop() {
fmt.Println("phone 停止")
}
func main() {
// 值类型可以赋值给接口
phone1 := Phone{Name: "小米手机"}
var p1 Usb = phone1
p1.Start()
// 指针类型也可以赋值给接口
phone2 := &Phone{Name: "苹果手机"}
var p2 Usb = phone2
p2.Start()
}
指针接收者
如果结构体中的方法是指针接收者 ,那么只有结构体指针类型 可以赋值给接口变量,结构体值类型不能赋值给接口变量。
go
package main
import "fmt"
type Usb interface {
Start()
Stop()
}
type Phone struct {
Name string
}
// 指针接收者
func (p *Phone) Start() {
fmt.Println(p.Name, "开始工作")
}
func (p *Phone) Stop() {
fmt.Println("phone 停止")
}
func main() {
// 错误写法:值类型不能赋值给接口
/*
phone1 := Phone{Name: "小米手机"}
var p1 Usb = phone1 // 编译错误
p1.Start()
*/
// 正确写法:指针类型可以赋值给接口
phone2 := &Phone{Name: "苹果手机"}
var p2 Usb = phone2
p2.Start()
}
六、一个结构体实现多个接口
Golang中一个结构体可以实现多个接口。
go
package main
import "fmt"
type AInterface interface {
GetInfo() string
}
type BInterface interface {
SetInfo(name string, age int)
}
type People struct {
Name string
Age int
}
func (p People) GetInfo() string {
return fmt.Sprintf("姓名:%v 年龄:%d", p.Name, p.Age)
}
func (p *People) SetInfo(name string, age int) {
p.Name = name
p.Age = age
}
func main() {
people := &People{
Name: "张三",
Age: 20,
}
// people 同时实现了 AInterface 和 BInterface
var p1 AInterface = people
var p2 BInterface = people
fmt.Println(p1.GetInfo())
p2.SetInfo("李四", 30)
fmt.Println(p1.GetInfo())
}
七、接口嵌套
接口与接口之间可以通过嵌套创建新的接口。
go
package main
import "fmt"
type SayInterface interface {
say()
}
type MoveInterface interface {
move()
}
// 接口嵌套
type Animal interface {
SayInterface
MoveInterface
}
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()
}