接口类型是对其它类型行为的抽象和概括;因为接口类型不会和特定的实现细节绑定在一起,通过这种抽象的方式我们可以让我们的函数更加灵活和更具有适应能力。
接口类型
接口类型具体描述了一系列方法的集合,一个实现了这些方法的具体类型是这个接口类型的实例。
io.Writer
类型是用得最广泛的接口之一,因为它提供了所有类型的写入bytes
的抽象。
// Writer is the interface that wraps the basic Write method.
//
// Write writes len(p) bytes from p to the underlying data stream.
// It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// Write must return a non-nil error if it returns n < len(p).
// Write must not modify the slice data, even temporarily.
//
// Implementations must not retain p.
type Writer interface {
Write(p []byte) (n int, err error)
}
实现接口
一个类型如果拥有一个接口需要的所有方法,那么这个类型就实现了这个接口。
如标准库中*os.File
类型实现了Reader
,Writer
,Closer
,和ReadWriter
接口。
举例子说明
抽象的东西请多动手 ✅
package main
import (
"fmt"
)
//Animal 定义 Animal 接口
type Animal interface {
Say()
}
//具体类型去实现接口所定义的方法
//Dog 狗狗
type Dog struct {
Name string
}
//Say 实现接口所需要的方法 就实现了接口
func (d Dog) Say() {
fmt.Println("Dog say :", d.Name)
}
//Cat 猫猫
type Cat struct {
Name string
}
//Say 实现接口所需要的方法 就实现了接口
func (c Cat) Say() {
fmt.Println("Cat say :", c.Name)
}
//String 实现 stringer接口
//fmt.Println 输出时 优先调用 String
func (c Cat) String() string {
return fmt.Sprintf("\nStringer Cat:%s", c.Name)
}
func main() {
// 一个接口变量
var i Animal
// 一个 Dog 变量
var d = Dog{
Name: "大狗狗",
}
//实现了接口就可以把值赋值给 接口
i = d
i.Say()
fmt.Printf("%T\n", i)
fmt.Println(i)
fmt.Println("------------------华丽分割线-------------")
// 一个 Cat 变量
var c = Cat{
Name: "小猫猫",
}
//实现了接口就可以把值赋值给 接口
i = c
i.Say()
fmt.Printf("%T", i)
fmt.Println(i)
}
结果
空接口
- 空接口
interface{}
任何类型都实现了空接口的方法因为没有方法 - 空接口用来存放任意类型的值如其他语言的
object
、void*
var i interface{} var d = Dog{ Name: "大狗狗", } i = d fmt.Println(i) i = "string" fmt.Println(i) i = false fmt.Println(i) i = 100 fmt.Println(i) i = 3.1415 fmt.Println(i)
断言
x.(T)
[变量.(类型)]被称为断言类型,这里x
表示一个接口的类型的值和T
表示一个类型。- 一个类型断言检查它操作对象的动态类型是否和断言的类型匹配。
这里有 2️⃣两种可能。
断言的类型
T
是一个具体类型,然后类型断言检查x
的动态类型是否和T
相同。
具体类型的类型断言从它的操作对象中获得具体的值。如果检查失败,这个操作会抛出panic
。var i Animal var d = Dog{ Name: "大狗狗", } i = d dog := i.(Dog) cat := i.(Cat) //panic: interface conversion: main.Animal is main.Dog, not main.Cat fmt.Println(dog, cat)
断言的类型T是一个接口类型,然后类型断言检查是否
x
的动态类型满足T
。
接口类型的类型断言改变了类型的表述方式,改变了可以获取的方法集合,但它保留了接口值内部的动态类型和值的部分。var i Animal var c = Cat{ Name: "小猫猫", } i = c a, ok := i.(Animal) fmt.Println(a, ok) a.Say() //a.String() //a.String undefined (type Animal has no field or method String)
如果断言操作的对象是一个nil
接口值,那么不论被断言的类型是什么这个类型断言都会失败。
对一个接口值的动态类型我们是不确定的,我们也不想产生panic
,那么可以返回一个额外的第二个结果,这个结果是一个标识成功与否的布尔值:
var i Animal
var d = Dog{
Name: "大狗狗",
}
i = d
dog, ok1 := i.(Dog)
cat, ok2 := i.(Cat)
fmt.Println(dog, ok1, cat, ok2)
//{大狗狗} true
//Stringer Cat: false
Comments