接口(interface)
基本概念:
在Go语言中,接口是一种类型,是一种抽象类型。它指定了一组方法签名。任何实现了这些方法签名的类型都自动实现了该接口,无需显式声明。接口是Go语言中实现多态的关键机制,也是其类型系统的一个核心特性。
接口的定义:
接口定义了一组方法,这些方法没有具体的实现。接口类型的变量可以存储任何实现了这些方法的类型的值。
type InterfaceName interface {
Method1(param1 Type1, param2 Type2) ReturnType1
Method2(param1 Type1, param2 Type2) ReturnType2
// 更多方法...
}
接口实现:
type ConcreteType struct {
// 字段定义
}
func (ct ConcreteType) Method1(param1 Type1, param2 Type2) ReturnType1 {
// 方法实现
}
func (ct ConcreteType) Method2(param1 Type1, param2 Type2) ReturnType2 {
// 方法实现
}
// ConcreteType 实现了 InterfaceName 接口
接口值:
接口值的定义
在Go语言中,接口值是指存储了实现某个接口的具体类型值的变量。接口值由两部分组成:
1.动态类型:这是接口值当前存储的实际类型。
2.动态值:这是实际存储的值,该值必须实现了接口中定义的所有方法
接口值的创建
接口值可以通过将任何实现了接口的具体类型的值赋给接口类型的变量来创建。
type Stringer interface {
String() string
}
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s (%d years)", p.Name, p.Age)
}
var s Stringer = Person{"Alice", 30}
s
是一个Stringer
接口值,它存储了一个Person
类型的值。
接口值的零值
当接口值没有被赋予具体的值时,它将有一个零值,即nil。nil接口值既没有动态类型也没有动态值。
var i InterfaceName
i 是一个InterfaceName接口的零值,它等于nil。
空接口
空接口的性质
空接口(interface{})是一种特殊的接口类型,因为它不包含任何方法。因此,所有类型都实现了空接口。
var any interface{} = 42 // 存储 int
any = "Hello, World!" // 存储 string
any = Person{"Bob", 25} // 存储 Person
空接口在Go中非常有用,因为它可以表示任何类型。
空接口典型实例:
func Println
func Println(a …interface{}) (n int, err error)
Println采用默认格式将其参数格式化并写入标准输出。总是会在相邻参数的输出之间添加空格并在输出结束后添加换行符。返回写入的字节数和遇到的任何错误。
该函数使用空接口作为形参,所以该函数可以接受所有类型作为形参
类型断言
在Go语言中,类型断言是一种操作,用于验证接口值是否存储了特定类型的值,并在断言成功时提取该值。类型断言通常包含在一个表达式中,该表达式的结果是一个新的值,该值要么是断言的类型,要么是零值。
类型断言的语法
value, ok := interfaceValue.(ConcreteType)
- interfaceValue:要进行断言的接口值。
- Type:要断言的类型。
- value:如果断言成功,将包含断言的类型的值。
- ok:如果断言成功,ok 将被设置为 true;如果断言失败,ok 将被设置为 false。
类型断言的用途
类型断言主要用于以下场景:
1.提取具体类型:当接口值存储了一个具体类型的值时,类型断言可以用来提取这个值。
2.类型检查:类型断言可以用来检查接口值是否存储了特定类型的
类型选择
类型选择是一种多态的语法结构,用于根据接口变量的具体类型执行不同的代码路径。
类型选择的语法
switch v := interfaceValue.(type) {
case Type1:
// 执行 Type1 的代码
case Type2:
// 执行 Type2 的代码
// ... 更多类型
default:
// 执行默认代码
}
- interfaceValue:要进行类型选择的接口值。
- type:用于匹配接口值动态类型的关键字。
- Type1, Type2, …:要匹配的具体类型。
- default:如果接口值不匹配任何类型,则执行这里的代码。
类型选择的用途:
1.根据类型执行代码:类型选择允许你根据接口值存储的具体类型执行不同的代码。
2.动态类型检查:类型选择可以在运行时检查接口值的动态类型。
接口组合
接口的组合是指将多个接口的方法组合成一个新接口的过程。这允许我们创建一个新接口,它包含了多个接口的方法
接口组合的语法
type CombinedInterface interface {
Interface1
Interface2
// ... 更多接口
}
作用场景
1.定义多态类型:组合接口允许你定义一个接口,它包含多个接口的方法,从而实现多态。
2.代码复用:通过组合多个接口,可以复用已有的接口定义,避免重复定义。
示例
type ReadWriter interface {
Reader
Writer
}
type File interface {
ReadWriter
Seeker
}
type T struct {
// 字段定义
}
func (t T) Read(p []byte) (n int, err error) {
// 实现 Read 方法
}
func (t T) Write(p []byte) (n int, err error) {
// 实现 Write 方法
}
func (t T) Seek(offset int64, whence int) (int64, error) {
// 实现 Seek 方法
}
// T 实现了 ReadWriter 和 File 接口
接口与指针接收者
如果一个接口的方法是通过指针接收者定义的,那么只有指向那个类型的指针才能实现该接口。
type Lock interface {
Lock()
Unlock()
}
type Mutex struct {
// ...
}
func (m *Mutex) Lock() {
// ...
}
func (m *Mutex) Unlock() {
// ...
}
// 只有 *Mutex 能实现 Lock 接口
接口注意事项:
1.接口里的所有方法都没有方法体,即接口的方法都是没有实现的方法。接口体现了程序设计的多态
和高内聚低偶合的思想。
2.Golang 中的接口,不需要显式的实现。只要一个变量,含有接口类型中的所有方法,那么这个变
量就实现这个接口。
3.接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量(实例)
4.接口中所有的方法都没有方法体,即都是没有实现的方法
5.在 Golang 中,一个自定义类型需要将某个接口的所有方法都实现,我们说这个自定义类型实现了该接口。
6.一个自定义类型只有实现了某个接口,才能将该自定义类型的实例(变量)赋给接口类型
7.只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型。
8.一个自定义类型可以实现多个接口
9.Golang 接口中不能有任何变量
10.一个接口(比如 A接口)可以继承多个别的接口(比如 B,C接口),这时如果要实现 A接口,也必须将 B.C 接口的方法也全部实现。
11.interface 类型默认是一个指针(引用类型),如果没有对 interface 初始化就使用,那么会输出 nil
12.空接口 interface{}没有任何方法,所以所有类型都实现了空接口,即我们可以把任何一个变量赋给空接口。