接口( interface)
接口是一种抽象的数据类型
1. 声明接口
接口本身是调用方和实现方均需要遵守的一种协议,接口是一种抽象的数据类型,不会暴露所含数据的格式、类型及结构。
接口声明的格式
type 接口类型名工 interface{
方法名1(参数) 返回值列表1
方法名2(参数) 返回值列表2
...
}
//Go语言在命名 时, 一般会在单词后面添 er ,
2. 实现接口的条件
2.1 接口的方法与实现接口的类型方法格式一致
(1)函数名不一致导致的报错
(2)实现接口的方法签名不一致导致的报错,签名包括方法中的名称、 参数数列表、返回参数列表。
2.2 接口中所有方法均被实现
当一个接口中有多个方法时,只有这些方法都被实现了,接口才能被正确编译并使用。
3. 理解类型与接口的关系
3.1 一个类型可以实现多个接口
一个类型可以同时实现多个接口,而接口间彼此独立,不知道对方的实现。
定义一个Socket类型的数据结构,定义两个方法用于数据的写入和资源的释放,用两个接口抽象的描述两个方法,他们的关系如图所示:
usingWriter()
和 usingCloser()
完全独立的 ,互相不知道对方存在,也不知道自己使用的 接口是 Socket实现现的。
3.2 多个类型可以实现相同的接口
package main
import (
"fmt"
)
// Service 接口定义了两个方法个是开启服务的方法( Start()), 个是输出日志的方法( Log())
type Service interface {
Start() error
Log(data string)
}
// 日志器
type Logger struct {
}
func (log Logger) Log(data string) {
fmt.Println(data)
}
// 游戏服务
type GameService struct {
Logger // 嵌入日志器
}
func (game GameService) Start() error {
return nil
}
func main() {
game := new(GameService)
game.Start()
game.Log("启动成功")
}
game
就可以使用 Start()
方法和 Log()
方法,其中, Start()
由GameService
实现, Log()
方法由Logger
实现。
4。接口查询
接口查询(Interface Type Assertion)是一种用于检查接口值底层类型的机制,以及如何将接口值转换为底层类型的过程。在Go中,可以使用接口查询来判断接口值是否包含特定类型的值,并将其转换为该类型。接口查询有两种形式:类型断言和类型判断。下面是它们的详细解释:
4.1 类型断言
类型断言允许你将接口值转换为具体的类型,前提是接口值确实包含了该类型的值。如果接口值不包含指定类型的值,类型断言将导致运行时恐慌(panic)。使用类型断言的语法如下:
value, ok := myInterface.(ConcreteType)
// myInterface 是要断言的接口值。
// ConcreteType 是目标类型。
// value 是接口值转换后的具体类型的值。
// ok 是一个布尔值,指示转换是否成功
-
4.2 类型判断
类型判断允许你使用switch语句检查接口值的底层类型,并根据类型执行相应的操作。这是一种更安全的方式,因为它不会导致运行时恐慌。例如:
var myInterface interface{} = 42
switch value := myInterface.(type) {
case int:
fmt.Println("Value is an int:", value)
case string:
fmt.Println("Value is a string:", value)
default:
fmt.Println("Value is of unknown type")
}
5. 接口组合
在Go语言中,接口组合(interface composition)是一种将多个接口合并成一个新接口的机制。通过接口组合,可以将多个接口的方法集合并在一起,形成一个新的接口,这个新接口包含了所有原始接口的方法。
接口组合的语法形式如下:
package main
import "fmt"
// 定义两个接口
type Reader interface {
Read()
}
type Writer1 interface {
Write()
}
// 将两个接口进行组合
type ReadWriter interface {
Reader
Writer1
}
// 实现接口
type File1 struct {
name string
}
func (f File1) Read() {
fmt.Printf("%s is reading...\n", f.name)
}
func (f File1) Write() {
fmt.Printf("%s is writing...\n", f.name)
}
func main() {
file := File1{name: "myFile"}
// ReadWriter 接口包含了 Reader 和 Writer 接口的方法
var rw ReadWriter = file
rw.Read()
rw.Write()
}
在上述示例中,我们首先定义了两个接口Reader和Writer1,分别包含了Read()和Write()方法。接着,我们将这两个接口进行组合,形成了新的接口ReadWriter,包含了Reader和Writer的所有方法。
然后,我们实现了一个File1类型,并为其实现了Read()和Write()方法。最后,在main()函数中,我们将File类型的实例赋值给ReadWriter类型的变量rw,并可以通过rw调用Read()和Write()方法。
通过接口组合,我们可以将多个接口的方法组合在一起,使得接口的使用更加灵活和方便。
6. 空接口类型( interface{})一一能保存所有值的类型
空接口是接口类型的特殊形式, 空接口没有任何方法,因此任何类型都无须实现空接口。从实现的角度看,任何值都满足这个接口的 。因为空接口类型可以保存任何值,也可以从空接口中取出原值。
** 6.1 将任何值保存到空接口中**
var any interface{}
any = 1
fmt.Printf("空接口any的值为%d,类型为%T\n", any, any)
any = "Hello"
fmt.Printf("空接口any的值为%s,类型为%T\n", any, any)
any = false
fmt.Printf("空接口any的值为%t,类型为%T\n", any, any)
运行结果
6.2 从空接口获取值
保存到空接口的值,如果直接取出指定类型的值时,会发生编译错误,解决这类错误需要用到类型断言。
var a int = 1![在这里插入图片描述](https://img-blog.csdnimg.cn/d50d8c40858d474090b1aae5c2745552.png#pic_center)
var i interface{} = a
var b int = i // 编译报错 :编译器告诉我们,不能将i变量视为 int 类型赋值给b
var b int = i.(int) //进行类型断言
变量i还是一个interface{}类型的变量。类似于无论集装箱装的是茶叶还是烟草,集装箱依然是金属做的,不会因为所装物的类型改变而改变, 解决上面的错误需要用到类型断言,将i变为int类型。
6.3 空接口的值比较
空接口在保存不同的值后,可以和其他变量值一样使用“==”进行比较操作,保存有类型不同的值的空接口进行比较时,Go语言会优先较值的类型。当接口中保存有动态类型的值时,运行时将触发错误,比如map类型和切片类型,这两种类型是不可进行比较的。
本文部分内容有参考部分书籍