基本概念
来自于官方文档的说明: An interface type defines a type set . A variable of interface type can store a value of any type that is in the type set of the interface. Such a type is said to implement the interface
翻译:一个接口类型定义一个类型集合。接口类型的变量可以存储接口类型集合中任何类型的值。这样的类型被认为是实现了接口
重点是后面这段话,我们来进行分段解释。
-
接口类型的变量可以存储接口类型集合中任何类型的值
这意味着如果你有一个接口,该接口定义了一些方法,那么任何具有这些方法的类型的值都可以被存储在该接口类型的变量中
-
这样的类型被认为是实现了接口
在Go中,类型不需要显示声明它实现了某个接口。只要类型具有接口所需的所有方法,它就被视为实现了接口。
案例: 假设我们有以下的接口定义:
go
type Duck interface {
shout() string
}
现在,我们定义了两个类型,它们都有 shout
方法:
go
type Dog struct {
name string
}
func (dg Dog) shout() string {
return dg.name + " is shouting"
}
type Bird struct {
name string
}
func (bd Bird) shout() string {
return bd.name + " is shouting"
}
由于Dog
和Bird
都有shout
方法,他们都实现了Duck
接口。因此,我们可以这样做:
go
func main() {
var duck Duck
duck = Dog{name: "Dog"}
fmt.Println(duck.shout()) // 输出:Dog is shouting
duck = Bird{name: "Bird"}
fmt.Println(duck.shout()) // 输出: Bird is shouting
}
在这个例子中,duck
是一个 Duck
接口类型的变量。尽管Dog
和Bird
是完全不同的类型,但由于它们都实现了Duck
接口,所以它们的实例都可以赋值给duck
变量,并调用它们的shout
方法。
- 接口不仅可以存储方法,还可以存储类型元素 我们先来看一个函数
go
func speak[T float64 | float32 | int | string | rune | byte] () {}
上述案例声明了一个泛型函数speak
,泛型T
它表示float64 | float32 | int | string | rune | byte
这些类型,如果还想为T
增加类型,便还需往后面添加,不美观也不便于检查。那么我们就可以使用interface
来表示。
go
type GeneralType interface {
float64 | float32 | int | string | rune | byte
}
func speak[T GeneralType] () {}
空接口
上文我们知道,如果一个类型满足接口的所有方法,该类型被认为实现了接口。当一个接口内没有定义任何方法(空接口),那么每个类型都被认为实现了它,使得它可以代表任何值。
go
var anything interface{}
anything = 100
fmt.Println(anything) // 输出:100
anything = "3.141592653589793"
fmt.Println(anything) // 输出:3.141592653589793
anything = true
fmt.Println(anything) // 输出:true
嵌入式接口(Embedded interfaces)
经典套娃环节又来了,Go允许我们在接口内部除了写类型元素和方法,还可以写接口。表示合并了接口内部的所有方法
go
type Dog interface {
walk()
}
type Bird interface {
shout()
}
type Animal interface {
Dog()
Bird()
}
Animal
接口嵌套了 Dog
和Bird
接口,相当于合并了Dog
接口和Bird
接口内部的所有方法。